4

I have two implementations of what I believe to be equivalent type class and instance definitions. The PureScript version builds without error but the Haskell version fails with the error Un-determined variables: e, f.

Can I make this work in Haskell?

Haskell:

class Foo a b c | a b -> c where
newtype Bar a b = Bar (a, b)
instance (Foo a c e, Foo b d f) => Foo (Bar a b) (Bar c d) (Bar e f) where

PureScript:

class Foo a b c | a b -> c
newtype Bar a b = Bar (Tuple a b)
instance (Foo a c e, Foo b d f) => Foo (Bar a b) (Bar c d) (Bar e f) 
tom
  • 85
  • 4

1 Answers1

8

You must enable UndecidableInstances; then your declaration will work. It's a bit subtle why this is required, i.e. how not enabling it could lead to a loop in the type-checker. But suppose in your program, you somehow caused the following constraint to need to be solved:

Foo (Bar a b) (Bar c d) a

The constraint solver would happily observe that we must choose the instance you wrote, and decide that therefore a ~ Bar a0 a1 for some a0 and a1, meaning we would like to solve the constraint:

Foo (Bar (Bar a0 a1) b) (Bar c d) (Bar a0 a1)

Now we can dispatch to the context, meaning we now need to solve the following two constraints:

( Foo (Bar a0 a1) c a0
, Foo b d a1
)

But wait! That first constraint is in the same form as the constraint we started with, but with different type variables. A loop!

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • Thanks Daniel, so PureScript allows undecidable instances by default? – tom Nov 03 '21 at 14:54
  • @tom Dunno, I've never used PureScript. Perhaps an actual expert can answer that for you. – Daniel Wagner Nov 03 '21 at 15:21
  • 2
    PureScript does indeed work similarly to `UndecidableInstances`. In Haskell, with UI enabled, the termination check is deferred to the use site, at which point trying to solve a looping constraint would produce an error saying "_Reduction stack overflow_". In PureScript, the same attempt results in error "_Type class instance for (blah) is possibly infinite_". I must admit, I don't know for sure how the PS compiler determines that: whether it just tries and sees if it loops for long enough (like GHC does) or there is a more clever way. – Fyodor Soikin Nov 03 '21 at 18:31