It’s non-obvious how I should be canonically getti...
# development
a
It’s non-obvious how I should be canonically getting the “type” of a
HydratedTarget
. Mumble mumble https://github.com/pantsbuild/pants/issues/4535 mumble mumble, but right now, I can either check the type of
HydratedTarget.adaptor
(really weird abstraction leak), or I can check the string value of
HydratedTarget.adaptor.type_alias
, except that’s non-canonical (maybe
PythonTest
, maybe
python_test
, maybe another alias someone has installed!)
w
yea... handwaving there. but now is a good time to be thinking about what fits the model.
another important concept is OptionalX ... then a rule could request
OptionalX
for a bunch of "possibly matching" things
a
As in, I have n targets, give me a
PyTestResults
if you can give it to me, but skip any targets you can’t give me a thing for?
w
right. the @console_rule would be requesting
OptionalTestResult
, perhaps
👋
a
That seems scary
a
i’m wary of introducing Optional as opposed to e.g. an enum type
w
Optional is an enum type 😃
@average-vr-56795: yea, unknown. but why would it be scary?
a
Optional is a fixed enum type that is easily overloaded for a variety of use cases. i had interpreted the above to mean there would be machinery for understanding Optional, but perhaps not an enum type I define. I think the concern is too tied to an impl in my head to worry about now
w
no magic around Optional... it would be like Collection
the switch statement concept from 4535 is the thing that needs "magic" (ie, native support)
a
i’ll check that out
a
Maybe not scary… But it confuses the model a bit… I will keep pondering…
w
a
i mean you’re using Optional and exceptions, which is fine, but also doesn’t give the opportunity for another rule to recover from a failure matching a target for a jvm package name
a
So I think the place it gets scary is when you’re aggregating different types. If my rule needs an
OptionalTestResult
at the top level, which comes from assuming that all targets either produced an
OptionalPythonTestResult
or an `OptionalJunitTestResult`… I guess I’d want some higher-order
Map
and
Merge
kind of rules to make that work; but I don’t really want to build a generic higher order function system into the graph… And if some targets were known not to produce any test result (say binaries listed as root targets, when I’m running test)… Basically, before even thinking about this, I’d want to address https://github.com/pantsbuild/pants/issues/4535 because I think all of the places I’d currently want optional are horribly muddied because how I’d actually want to solve them is per-target-type Target types.
After addressing #4535 I can see maybe wanting
Optional
, but I don’t want to patch over #4535 with
Optional
w
per-target-type Target types.
yea. my feeling is that 4535 allows for that
but i don't think Optional alone is enough anyway. you can think of the switch statement as being an unambiguous switch that returns an OptionalTestResult
unless you have exactly one test type, Optional doesn't get you all the way there
because a statement like
Get(OptionalTestResult, Address(..))
would be ambiguous without something to switch
a
i feel like the dependency resolution we have with declarative selectors is really powerful and i still don't know why we need an enum type or a switch or any type of casting or any canonical monadic types like Optional (which simulate the logic we already have in the graph) instead of a rule subgraph with intermediate products. then instead of having to worry about how to structure optionals that maybe contain extra info and merging them we can just make sure your rule (which does whatever testing it wants) produces a collection of
class CompletedTestResult(datatype(...))
, or maybe a wrapper
class JunitCompletedTestResult(datatype([('test_result', CompletedTestResult)]))
around that collection which has a rule subgraph to convert it into a
CompletedTestResult
so the polymorphism is done by the logic the graph already contains. the only reason this isn't immediately doable (it seems) is because trivial rule subgraphs e.g. accessing a field still invoke some overhead, so we can't just willy-nilly create these simple synchronous rule subgraphs e.g. to extract the
test_result
field above (or so john was saying at one point in a PR i made?), but that's a fixable problem.
w
did you see my response on 4535
a
now i did
don't respond to the above
commented on the issue. this idea isn't something i just made up and it is different from the way we are currently approaching it and it is also quite specific
and i'm being intense about it because this is a quality i would very very very much like to preserve, is non-reliance on special types you have to subclass, and that's only so feasible because of the current rule graph
w
i am not suggesting subtyping.
a
and non-reliance on special types isn't a feature of the framework it's a feature of the ruleset
ok, that's true
w
your response does not address the extensibility bit: concretely, how are you suggesting that we implement extensibility of a series of
if
statements
can reply there
(please)
a
i don't understand how it doesn't address that. the rule graph is extensible
w
if there is already a rule to satisfy
X for Y
, installing another rule to do that will cause the graph to fail to build with an ambiguity error
a
yep
w
it will do that statically
a
that's correct
w
(it used to do it at runtime, sortof, somewhat. but the new/old rules are the same)
which means that as it stands, it is not extensible. a plugin cannot add a new TestResult type.
or a new provider of TestResult.
a
the framing of the problem seems incredibly narrow
and that's incorrect, a plugin can write rules to provide TestResult or anything else unless i'm very confused
w
you are.
see my last few comments again
if there is a rule to get
X for Y
, you may not install another
a
yes
w
so, concretely: if there is a pytest runner rule installed, you may not install a junit runner
...without a switch statement.
a
what is this pytest runner rule selecting
and the junit runner
w
and, a switch statement that replaces some existing built in rule
what is this pytest runner rule selecting
doesn't matter.... what matters is that the test
@console_rule
needs to not need to know about "all possible runners"
a
@rule(TestResult, [Select(PytestConfig)])
and
@rule(TestResult, [Select(JunitConfig)])
w
it needs to request something abstract, which then needs to be filled in by multiple implementations
that is not extensible
@aloof-angle-91616: maybe write a real code snippet showing what you mean.
a
i've got plenty of code lol
i'm making a reply
w
@rule(TestResult, [Select(PytestConfig)])
and
@rule(TestResult, [Select(JunitConfig)])
the issue with this is that you still need the switch statement somewhere to create those configs
a
it's turtles all the way down actually
i posted a code sample with ellipses in only the right places to #4535
w
as i said: there is nothing to provide
JunitConfig
a
why wouldn’t that be provided by the junit ruleset?
w
how would something know that it could provide
JunitConfig
for some address?
a
because there’s a unique path in the rule graph from something -> JunitConfig
w
and to get back to the title of the ticket: this all started because we have a concrete case of this: you don't know till runtime whether a target will be an X or a Y
a
i can write that code too
yes
but the switching doesn’t have to be forced onto the rule writer it can be isolated onto specifically grabbing things from targets which i thought was the point of hydrated fields etc
w
the whole point of the last N comments on the ticket is that we do not have a facility to do that for the rule writer
the point of the switch-statement-facility would be to avoid having to write a rule representing a switch statement
which is impossible to do extensbily
a
if we have a rule from BuildFileAddresses -> Collection.of(JunitCompatibleTargetWrapper) we can ensure the switching is kept in the process of target selection instead of introducing optional into test results
impossible is a strong word
and i don’t believe that’s correct as stated lol
like the dynamic parts are options and targets and other things the user interacts with and those can be switched on for sure because that’s how all functional programs work
not trying to talk down to you
i’m not a functional programmer