
On Tue, Feb 23, 2021 at 08:09:21PM +0000, CASANOVA Juan wrote:
please someone do let me know if at some point my posts feel excessive or out of place.
I think your questions are perfectly reasonable for this list and many people here, including me, will enjoy thinking about them and answering them. My suggestion is purely to point out that, in practical terms, there are probably much more direct and simple routes to achieving your goal. It is a rare problem that gets simpler by the addition of type classes. That being said, there's also a lot of value in deeply exploring different approaches even if they don't turn out to be practical in the end. Tom
________________________________ From: Haskell-Cafe
on behalf of Tom Ellis Sent: 23 February 2021 19:27 To: haskell-cafe@haskell.org Subject: Re: [Haskell-cafe] DatatypeContexts / alternative This email was sent to you by someone outside the University. You should only click on links or attachments if you are certain that the email is genuine and the content is safe.
On Tue, Feb 23, 2021 at 06:37:06PM +0000, CASANOVA Juan wrote:
Yes I realize that, but what I am expecting, I guess, is for the type checker to tell me (or anyone who tried to use it) that Foo (IO String) makes no sense, because Foo is always applied to something with Ord. That the very concept of the type Foo (IO String) itself does not type check.
I realize the answer might be that this is impossible with current Haskell, but then a) Is there any fundamental reason why it is impossible
Yes, there's a fundamental reason. Neither you nor the compiler can possibly know when invalid Foos (i.e. those whose parameter isn't Ord) are constructed inside a function polymorphic in a Functor . Let's extend my example.
mapToUnit :: Functor f => f a -> f () mapToUnit = fmap (const ()) . fmap (const getLine)
whatShouldItDo :: Foo () whatShouldItDo = mapToUnit (Foo (Data.Map.singleton () ()))
Do you agree that whatShouldItDo is well typed? If not, why not? If so, how should it behave when evaluated? The implementation of mapToUnit , combined with your Functor instance says that fromList must be run on [(getLine, getLine)] . But this is impossible!
Accepting that whatShouldItDo is well typed implies that you must accept that your Functor instance for Foo cannot work. (I don't believe any instance can work if it is law-abiding but I don't know of a proof.)
There is a cottage industry of trying to work around this problem. See, for example [1]. I have never seen a satisfactory solution.
Based on your posts here you are frequently running into the limits of type class based programming in Haskell. If your purpose is practical I strongly advise you to stay away from type classes for anything non-trivial. Stick to the value level. Your life will be much easier. If your purpose is research into type classes, or general interest, then good luck and enjoy!
Tom
[1] https://jeltsch.wordpress.com/2015/09/03/constrained-monads/
________________________________ From: Haskell-Cafe
on behalf of Tom Ellis Sent: 23 February 2021 18:29 To: haskell-cafe@haskell.org Subject: Re: [Haskell-cafe] DatatypeContexts / alternative This email was sent to you by someone outside the University. You should only click on links or attachments if you are certain that the email is genuine and the content is safe.
On Tue, Feb 23, 2021 at 06:14:59PM +0000, CASANOVA Juan wrote:
module DatatypeContextsExample where
import Data.Map import Data.Bifunctor
data Foo t = Foo (Map t t)
instance Functor Foo where fmap f (Foo m) = Foo (fromList (fmap (bimap f f) (toList m)))
This does not compile, because I am using toList and fromList, which require (Ord t). But I cannot really have Foo be a functor in this way without it. The thing is, every time I am going to use it, t is actually going to be Ord. But how do I tell Haskell to have this constraint?
You say that every time you are going to use it t is actually going to be Ord, but how does the compiler know that when it comes to type check fmap? After all, somewhere else you could write
fmap (const getLine) :: Foo t -> Foo (IO String)
and IO String is certainly not Ord.