-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inconsistency in effect and effect extension method declarations results in inconsistent compiler behavior and false positive compilation #22
Comments
Thanks @jackcviers for such detailed examples.
I think so, we only need type classes in cases like
Yes, I think that would be better and ideally, when we use parametric types like Having said all this we still need to discuss the multiplatform/multilanguage approach since this is just specific to Scala but the plan is to make all these abilities cross-language yet compatible while using the best features from each lang. For example context functions in Scala, context receivers in kotlin, and functional interfaces with lambda arguments in Java. |
It is necessary, tagging and refined types are especially common use cases when doing serialization. We'd need to provide the evidence that a You could of course use You could use value types, too. But you'll have to box in collections. You can't move it to the method in the case of serialization, because you usually end up having to write pre-value schema definition stuff encoded in some bitmask prior to encoding the bytes (or use Protobuf and a mutable header or Avro and some header definition file or some other serialization tech), which means knowing the type before choosing the serialization strategy or otherwise having a priori knowledge to deduce a strategy from the available ones. I chose We'll often need parameters in the effect declaration, but not need anything inside the effect at runtime. We might be able to get around needing that using Dependent function types, too, though. Haven't tried it yet, but might be something else worth exploring.
I didn't try that encoding |
In Java I assume we'd use annotation processing rather than lamda arguments, as that would occur at compile time. You would have to create maven and gradle plugins for them, of course, but they'd be pretty much boilerplate. You might need to use AOP interceptors to suspend everything into the continuation as well. But it would work (on loom). For JS/Typescript, you're probably looking at a babel plugin for the compiler verified effects stuff. All the cool libs do it -- react, angular, etc. No reason not to extend the language when everyone else does. I'd probably suggest limiting it to typescript instead of going full js. Then be prepared when in two or three years they want to standardize it. You could probably embed the continuations all in async/await, but that's not 1:1 behavior with what we're really talking about here, because it would be eager and not wait for Python would be a good target lang as well. You'd probably use decorators and metaclasses to do this stuff there. It wouldn't have much effect if somebody didn't run a typer, but if they did it would really help. hypothesis already does stuff like this, as does marshmallow. Plus it has real 0-cost newtypes, so the erased classes are a cinch. It already has fiber-like things in many different libraries. Rust and swift I have no idea how we'd implement things like erased classes within. Rust has macros for the compiler checking/derivation things, and stackless coroutines for the continuations. I have no idea how complex they'd be to implement an exact copy of the behavior within. I've heard mixed reviews of using async/await in rust, however. Swift has path dependent types -- so you could use the Aux pattern from old shapeless and it has Extensions. I don't know what support it has for suspended and labeled continuations though. |
The required context gets lost when defining methods on an effect as extension methods, but not when an extension is defined as a top-level extension method. When defined as a typeclass, the effect type cannot be inferred.
Case-1
Given the following definition of an effect:
And a test file:
The compiler will fail with the following message:
Case-2
However, if we modify the definition of
Serde[A]
to be an empty marker trait (not a phantom type because it is instantiated when the given instance is provided) so that we can take advantage of other capabilities in the return of any extension methods related to theSerde[A]
capability, such asErrors
:and we don't change the definition of the client code in the tests, we get an entirely different (and, in fact, the desired compilation error) compilation error result:
Providing a
Serde[String]
instance resolves the error and the effect works as intended in Case 2:Case-2 Resolved Given Context:
Case-3
A false-positive compilation occurs when encoding
Serde[A]
as an opaque type. When we encodeopaque type Serde[A] = Unit
, the top-level extension methods find and use the top-level givenRuntime
instance instead of failing to compile with a missingSerde[String]
capability:The client test code remains the same. The property tests pass. This should fail compilation, but doesn't because the extension methods exist at the top-level:
This really confused me, as it appears to be defined in the same manner as
fx.Bind
, and that will fail compilation correctly. I don't have a good explanation for why this occurs.Case-3A
Using the same effect definition as in Case 3 but with the required capability declared on the actual value in the property tests results in a missing capability error at the point of
run
in the test, but loses the capability type parameter in the implicit not found definition:Case 5
Of course, if we provide a given definition for
Serde[String]
, the test compiles and passes correctly, with or without the actual value type annotation, and implicit expansion shows the correctgiven_Serde_String
given instance being used for the Serde instance:Client Test Example Un-annotated
Client Test Example Annotated
Results (identical for annotated and un-annotated test file)
Conclusion
We currently use both styles: typeclasses and top-level extension-methods, in
scala-fx
. We also currently use bath opaque and trait effect declaration types. However, given the interference of the scala compiler when using typeclass definitions, might it be better to avoid them in general? And given the compiler behavior when using opaque types, might it be better to stick to empty trait givens with top-level extensions to avoid the incorrect compilation and error message results?The text was updated successfully, but these errors were encountered: