inference with implicitparams seems broken

Hello everyone, I wonder why the following taken from an stackoverflow answer doesn't work: https://stackoverflow.com/questions/71184922/is-it-possible-to-infer-a-type-.... Code of the answer posted here verbatim: ``` {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE RankNTypes #-} module Temp where withX :: Int -> ((?x :: Int) => r) -> r withX x r = let ?x = x in r somethingThanNeedsX :: (?x :: Int) => Int somethingThanNeedsX = ?x + 2 foo :: Int foo = bar where bar = withX 42 baz baz = somethingThanNeedsX ``` This won't compile with the error message: ``` Orig.hs:19:11: error: • Unbound implicit parameter (?x::Int) arising from a use of ‘somethingThanNeedsX’ • In the expression: somethingThanNeedsX In an equation for ‘baz’: baz = somethingThanNeedsX In an equation for ‘foo’: foo = bar where bar = withX 42 baz baz = somethingThanNeedsX | 19 | baz = somethingThanNeedsX ``` If you give `baz` a type signature which includes the implicit param it compiles. But I think this is a bug. GHC should not require a type signature here. I tried searching for a GHC ticket but I couldn't find one. But that might also be down to me not using the right query. Anyway my question is, is this expected behavior? This really makes implicit params less ergonomic. Best regards, Rowan Goemans

It’s due to the monomorphism restriction [1]. If you give `baz` an argument it does infer the type correctly: ``` foo :: Int foo = bar where bar = withX 42 (baz ()) baz () = somethingThanNeedsX ``` Or you can disable the monomorphism restriction: ``` {-# LANGUAGE NoMonomorphismRestriction #-} ``` [1] https://wiki.haskell.org/Monomorphism_restriction
On 23 Mar 2022, at 15:48, rowan goemans
wrote: Hello everyone,
I wonder why the following taken from an stackoverflow answer doesn't work: https://stackoverflow.com/questions/71184922/is-it-possible-to-infer-a-type-.... Code of the answer posted here verbatim:
``` {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE RankNTypes #-}
module Temp where
withX :: Int -> ((?x :: Int) => r) -> r withX x r = let ?x = x in r
somethingThanNeedsX :: (?x :: Int) => Int somethingThanNeedsX = ?x + 2
foo :: Int foo = bar where bar = withX 42 baz
baz = somethingThanNeedsX ```
This won't compile with the error message:
``` Orig.hs:19:11: error: • Unbound implicit parameter (?x::Int) arising from a use of ‘somethingThanNeedsX’ • In the expression: somethingThanNeedsX In an equation for ‘baz’: baz = somethingThanNeedsX In an equation for ‘foo’: foo = bar where bar = withX 42 baz baz = somethingThanNeedsX | 19 | baz = somethingThanNeedsX ```
If you give `baz` a type signature which includes the implicit param it compiles. But I think this is a bug. GHC should not require a type signature here. I tried searching for a GHC ticket but I couldn't find one. But that might also be down to me not using the right query.
Anyway my question is, is this expected behavior? This really makes implicit params less ergonomic.
Best regards,
Rowan Goemans
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Indeed, with the monomorphism restriction disabled it works without issues. is this by design/expected though? Morally I feel it is already monomorph, it's just missing an argument which needs to be provided. Would there be interest in fixing this in GHC? On 3/23/22 15:56, J. Reinders wrote:
It’s due to the monomorphism restriction [1]. If you give `baz` an argument it does infer the type correctly:
``` foo :: Int foo = bar where bar = withX 42 (baz ()) baz () = somethingThanNeedsX ```
Or you can disable the monomorphism restriction:
``` {-# LANGUAGE NoMonomorphismRestriction #-} ```
[1] https://wiki.haskell.org/Monomorphism_restriction
On 23 Mar 2022, at 15:48, rowan goemans
wrote: Hello everyone,
I wonder why the following taken from an stackoverflow answer doesn't work: https://stackoverflow.com/questions/71184922/is-it-possible-to-infer-a-type-.... Code of the answer posted here verbatim:
``` {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE RankNTypes #-}
module Temp where
withX :: Int -> ((?x :: Int) => r) -> r withX x r = let ?x = x in r
somethingThanNeedsX :: (?x :: Int) => Int somethingThanNeedsX = ?x + 2
foo :: Int foo = bar where bar = withX 42 baz
baz = somethingThanNeedsX ```
This won't compile with the error message:
``` Orig.hs:19:11: error: • Unbound implicit parameter (?x::Int) arising from a use of ‘somethingThanNeedsX’ • In the expression: somethingThanNeedsX In an equation for ‘baz’: baz = somethingThanNeedsX In an equation for ‘foo’: foo = bar where bar = withX 42 baz baz = somethingThanNeedsX | 19 | baz = somethingThanNeedsX ```
If you give `baz` a type signature which includes the implicit param it compiles. But I think this is a bug. GHC should not require a type signature here. I tried searching for a GHC ticket but I couldn't find one. But that might also be down to me not using the right query.
Anyway my question is, is this expected behavior? This really makes implicit params less ergonomic.
Best regards,
Rowan Goemans
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

On Mar 24, 2022, at 9:04 AM, rowan goemans
wrote: is this by design/expected though?
It is by design, yes. With a sufficiently nuanced expectation, I would also say it's expected. (Though, to be fair, if I were not primed to be thinking about the monomorphism restriction, I can't honestly say I would get it right if quizzed.)
Would there be interest in fixing this in GHC?
Figuring out when to generalize a local binding is a hard problem. So, there is definitely interest in finding a better way to do it, but I don't think anyone knows a design that meets the most expectations. Language design is hard! :) Richard

Hi Richard, Thanks for answering. I have made a tiny change to GHC in a fork that lifts the monomorphization restriction but only for implicit params. See this commit: https://gitlab.haskell.org/rowanG/ghc/-/merge_requests/1/diffs?commit_id=35f... Is there some example I could run to see in action why the monomorphisation restriction is required? I looked at the Haskell report and I don't immediately see why the points raised apply to implicit params. I get a feeling that monomorph(Contains only a concrete type like Int or Bool) implicit params would never be a problem and maybe only polymorphic ones are? Rowan Goemans On 3/24/22 20:05, Richard Eisenberg wrote:
On Mar 24, 2022, at 9:04 AM, rowan goemans
wrote: is this by design/expected though?
It is by design, yes. With a sufficiently nuanced expectation, I would also say it's expected. (Though, to be fair, if I were not primed to be thinking about the monomorphism restriction, I can't honestly say I would get it right if quizzed.)
Would there be interest in fixing this in GHC?
Figuring out when to generalize a local binding is a hard problem. So, there is definitely interest in finding a better way to do it, but I don't think anyone knows a design that meets the most expectations. Language design is hard! :)
Richard

Hi Rowan, The problem is that generalizing a let-binding turns something that looks like a constant into a function. This means that repeated use of the variable will evaluate it separately each time. This can be observed to slow down a program. A key point of the monomorphism restriction is to prevent non-functions from actually being functions. This applies equally well to implicit parameters as to other constraints. Richard
On Mar 24, 2022, at 5:37 PM, rowan goemans
wrote: Hi Richard,
Thanks for answering. I have made a tiny change to GHC in a fork that lifts the monomorphization restriction but only for implicit params. See this commit: https://gitlab.haskell.org/rowanG/ghc/-/merge_requests/1/diffs?commit_id=35f... https://gitlab.haskell.org/rowanG/ghc/-/merge_requests/1/diffs?commit_id=35f... Is there some example I could run to see in action why the monomorphisation restriction is required? I looked at the Haskell report and I don't immediately see why the points raised apply to implicit params. I get a feeling that monomorph(Contains only a concrete type like Int or Bool) implicit params would never be a problem and maybe only polymorphic ones are?
Rowan Goemans
On 3/24/22 20:05, Richard Eisenberg wrote:
On Mar 24, 2022, at 9:04 AM, rowan goemans
mailto:goemansrowan@gmail.com> wrote: is this by design/expected though?
It is by design, yes. With a sufficiently nuanced expectation, I would also say it's expected. (Though, to be fair, if I were not primed to be thinking about the monomorphism restriction, I can't honestly say I would get it right if quizzed.)
Would there be interest in fixing this in GHC?
Figuring out when to generalize a local binding is a hard problem. So, there is definitely interest in finding a better way to do it, but I don't think anyone knows a design that meets the most expectations. Language design is hard! :)
Richard
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Hi Richard, I understand why the monomorphism restriction is in place. But I feel the reason about potentially repeated computations which are non-obvious aren't really applicable to implicit params. If you use a function with a different implicit param then I don't think anyone would be surprised that it is a different computations. Maybe I'm just not seeing it though. E.g. this is confusing if monomorphism restriction isn't enabled: ``` plus a b = a + b res = plus 6 10 usage1 :: Int usage1 = res usage2 :: Integer usage2 = res ``` as the potentially expensive `plus` computation is ran twice where a user wouldn't expect it to be evaluated twice. But something comparable with implicit params isn't confusing I think: ``` plus a = a + ?b res = plus 6 usage1 :: Int usage1 = let ?b = 10 in res usage2 :: Integer usage2 = let ?b = 10 in res ``` My main point though is whether this was something that was already discussed somewhere with the conclusion being that implicit params should really fall under the monomorphism restriction. Instead of it being an artifact of the current implementation in GHC. Because I would be quite interested in lifting it for implicit params. Rowan Goemans On 3/25/22 20:17, Richard Eisenberg wrote:
Hi Rowan,
The problem is that generalizing a let-binding turns something that looks like a constant into a function. This means that repeated use of the variable will evaluate it separately each time. This can be observed to slow down a program. A key point of the monomorphism restriction is to prevent non-functions from actually being functions. This applies equally well to implicit parameters as to other constraints.
Richard
On Mar 24, 2022, at 5:37 PM, rowan goemans
wrote: Hi Richard,
Thanks for answering. I have made a tiny change to GHC in a fork that lifts the monomorphization restriction but only for implicit params. See this commit: https://gitlab.haskell.org/rowanG/ghc/-/merge_requests/1/diffs?commit_id=35f...
Is there some example I could run to see in action why the monomorphisation restriction is required? I looked at the Haskell report and I don't immediately see why the points raised apply to implicit params. I get a feeling that monomorph(Contains only a concrete type like Int or Bool) implicit params would never be a problem and maybe only polymorphic ones are?
Rowan Goemans
On 3/24/22 20:05, Richard Eisenberg wrote:
On Mar 24, 2022, at 9:04 AM, rowan goemans
wrote: is this by design/expected though?
It is by design, yes. With a sufficiently nuanced expectation, I would also say it's expected. (Though, to be fair, if I were not primed to be thinking about the monomorphism restriction, I can't honestly say I would get it right if quizzed.)
Would there be interest in fixing this in GHC?
Figuring out when to generalize a local binding is a hard problem. So, there is definitely interest in finding a better way to do it, but I don't think anyone knows a design that meets the most expectations. Language design is hard! :)
Richard
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Hi Rowan, Thanks for the detailed answer. For me, there is always a danger in the possibility that I might not realize there is an implicit param in play, but I see your point. To my knowledge, this particular question has not been considered: whether (and how) the MR should apply to implicit parameters. My recommendation, if you want to pursue this, is to post at the ghc-proposals repo. If you're not yet sure exactly what behavior you think would be better, I encourage you to make an Issue first. Issues tend to get good discussion going. Then, once there is some consensus about future directions, you (or someone else) can make a formal proposal to change this behavior. It's true that the implementation change is easy (I agree with your code), but specification is not so easy sometimes. :) Richard
On Mar 28, 2022, at 8:08 AM, rowan goemans
wrote: Hi Richard,
I understand why the monomorphism restriction is in place. But I feel the reason about potentially repeated computations which are non-obvious aren't really applicable to implicit params. If you use a function with a different implicit param then I don't think anyone would be surprised that it is a different computations. Maybe I'm just not seeing it though. E.g. this is confusing if monomorphism restriction isn't enabled:
``` plus a b = a + b
res = plus 6 10
usage1 :: Int usage1 = res
usage2 :: Integer usage2 = res ```
as the potentially expensive `plus` computation is ran twice where a user wouldn't expect it to be evaluated twice.
But something comparable with implicit params isn't confusing I think:
``` plus a = a + ?b
res = plus 6
usage1 :: Int usage1 = let ?b = 10 in res
usage2 :: Integer usage2 = let ?b = 10 in res ```
My main point though is whether this was something that was already discussed somewhere with the conclusion being that implicit params should really fall under the monomorphism restriction. Instead of it being an artifact of the current implementation in GHC. Because I would be quite interested in lifting it for implicit params.
Rowan Goemans
On 3/25/22 20:17, Richard Eisenberg wrote:
Hi Rowan,
The problem is that generalizing a let-binding turns something that looks like a constant into a function. This means that repeated use of the variable will evaluate it separately each time. This can be observed to slow down a program. A key point of the monomorphism restriction is to prevent non-functions from actually being functions. This applies equally well to implicit parameters as to other constraints.
Richard
On Mar 24, 2022, at 5:37 PM, rowan goemans
mailto:goemansrowan@gmail.com> wrote: Hi Richard,
Thanks for answering. I have made a tiny change to GHC in a fork that lifts the monomorphization restriction but only for implicit params. See this commit: https://gitlab.haskell.org/rowanG/ghc/-/merge_requests/1/diffs?commit_id=35f... https://gitlab.haskell.org/rowanG/ghc/-/merge_requests/1/diffs?commit_id=35f... Is there some example I could run to see in action why the monomorphisation restriction is required? I looked at the Haskell report and I don't immediately see why the points raised apply to implicit params. I get a feeling that monomorph(Contains only a concrete type like Int or Bool) implicit params would never be a problem and maybe only polymorphic ones are?
Rowan Goemans
On 3/24/22 20:05, Richard Eisenberg wrote:
On Mar 24, 2022, at 9:04 AM, rowan goemans
mailto:goemansrowan@gmail.com> wrote: is this by design/expected though?
It is by design, yes. With a sufficiently nuanced expectation, I would also say it's expected. (Though, to be fair, if I were not primed to be thinking about the monomorphism restriction, I can't honestly say I would get it right if quizzed.)
Would there be interest in fixing this in GHC?
Figuring out when to generalize a local binding is a hard problem. So, there is definitely interest in finding a better way to do it, but I don't think anyone knows a design that meets the most expectations. Language design is hard! :)
Richard
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
participants (3)
-
J. Reinders
-
Richard Eisenberg
-
rowan goemans