-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Simplify FunctionK.lift macro #3402
Conversation
Codecov Report
@@ Coverage Diff @@
## master #3402 +/- ##
=======================================
Coverage 92.70% 92.71%
=======================================
Files 379 379
Lines 7981 7984 +3
Branches 230 216 -14
=======================================
+ Hits 7399 7402 +3
Misses 582 582
Continue to review full report at Codecov.
|
* Note: The weird `τ[F, G]` parameter is there to compensate for | ||
* the lack of polymorphic function types in Scala 2. | ||
*/ | ||
def liftFunction[F[_], G[_]](f: F[τ[F, G]] => G[τ[F, G]]): FunctionK[F, G] = new FunctionK[F, G] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we entirely sure this is sound? I know that similar tricks involving a *
-kinded tau are not, but I find it difficult to reason about scalac's behavior in these cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took it from the discussion in #2553. We could also call it unsafeLift
but I guess it would have to be public because it's called in the expanded macro.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIU because F
and G
are type parameters of τ
you cannot reuse it with nested FunctionK
s which would be a way to exploit unsoundness if we had a bare τ
without type parameters.
bc1b7b3
to
c204ae4
Compare
Codecov Report
@@ Coverage Diff @@
## master #3402 +/- ##
=======================================
Coverage 91.30% 91.30%
=======================================
Files 386 386
Lines 8565 8565
Branches 255 263 +8
=======================================
Hits 7820 7820
Misses 745 745 |
|
||
test("lift a function directly") { | ||
def headOption[A](list: List[A]): Option[A] = list.headOption | ||
val fHeadOption = FunctionK.liftFunction[List, Option](headOption) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A question for my understanding, could this be
val fHeadOption = FunctionK.liftFunction[List, Option](headOption) | |
val fHeadOption = FunctionK.lift[List, Option](headOption) |
Isn't it forwarding to liftFunction
anyway ? Or it's testing the public liftFunction
explicitly on purpose ?
Apologies if this doesn't make sense :).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's testing liftFunction
explicitly. Do you think we should also try to add a dotty version? It has native polymorphic functions, but we don't provide a way to convert them to FunctionK
and I think type inference is still not great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense, thanks for clarifying. About the dotty version, I guess it's related to #2553 that's still an open issue for what I understand and specifically there is work going on as note here #2553 (comment). We could sync a dotty specific version with that effort.
So what do you thing (you and others) about letting this PR move on as it improves the current situation described here #3400 and might make some user's life easier ? My approval goes in that direction. Of course I happy to do otherwise if the general preference is different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I agree this can be merged and I can try to add a dotty version in another PR 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brilliant, thank 🙇
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this. Hopefully we can find a dotty solution soon as well
FYI I actually have a Dotty solution already, but it doesn't compile because higher-rank type inference isn't fully implemented. @smarter has mentioned that this is something he wants to do, but he hasn't had the time. |
Yes, that's what I meant, type inference doesn't work. I'm not sure if this can be solved with a macro as in Scala 2 but we can at least have |
@djspiewak , sorry for the ping, this PR has two approvals so if you're happy with the changes and the conflicts are resolved I would merge it in. |
Add `FunctionK.liftFunction` that uses an abstract type member to emulate a polymorphic function type. Because `liftFunction` has worse type inference, keep the macro based `lift` that now delegates to `liftFunction`, instead of creating a new `FunctionK` instance every time. Also accept eta-expanded functions in the macro implementation.
c204ae4
to
78976a5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Add
FunctionK.liftFunction
that uses an abstract type memberto emulate a polymorphic function type.
Because
liftFunction
has worse type inference,keep the macro based
lift
that now delegates toliftFunction
,instead of creating a new
FunctionK
instance every time.Also accept eta-expanded functions in the macro implementation.
Fixes #3400