3

The following code doesnt compile:

class Foo f where
  getInt :: f -> Int

class Bar b where
  getFooFromBar :: Foo f => b -> f

someFunction :: Bar b => b -> Int
someFunction bar = getInt $ getFooFromBar bar

The error is Could not deduce (Foo f) arising from a use of 'getInt' from the context (Bar b)

I know I can fix the error by changing the class for Bar as follows:

class Foo f => Bar f b where
  getFooFromBar :: b -> f

But I would prefer if I didnt have to add f to all the instance signatures of Bar.

Is there a way to do this by only keeping the Foo f constraint only on the getFooFromBar signature, rather than the whole class?

tmortiboy
  • 485
  • 3
  • 10
  • 3
    I recommend you reconsider the type of `getFooFromBar` - it looks poised for an `Ambiguous type variable` error. – Alec Nov 04 '16 at 02:45
  • 1
    @Alec The full error indeed says `The type variable ‘f0’ is ambiguous`. – duplode Nov 04 '16 at 02:59
  • Possible duplicate of [Understanding a case of Haskell Type-Ambiguity](http://stackoverflow.com/questions/21220655/understanding-a-case-of-haskell-type-ambiguity) – Cactus Nov 04 '16 at 04:56

1 Answers1

5

But I would prefer if I didnt have to add f to all the instance signatures of Bar.

Technically speaking, you don't need to do that to make your example compile. You can use a type annotation to specify which instance of Foo you are using in someFunction, solving the ambiguous type variable error. However, you have a deeper problem:

class Foo f where
  getInt :: f -> Int

class Bar b where
  getFooFromBar :: Foo f => b -> f

That is, for all practical purposes, impossible. The type of getFooFromBar says you can use it to produce a result of any type f that has an instance of Foo. But how will you materialise this value for any f? It is no use reaching for any specific instance when defining getFooFromBar, as all you will get from that is a Couldn't match type ‘f’ with ‘Blah’ type error. The immediate solution to that is the one you suggested, for a different reason, in the question: specifying the instance of Foo to use through the Bar instance. You might find it nicer to do with a type family, rather than a multi-parameter type class:

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}

class Foo f where
  getInt :: f -> Int

class Foo (FooBar b) => Bar b where
  type FooBar b :: *
  getFooFromBar :: b -> FooBar b

instance Foo Char where
   getInt = const 99

instance Bar Char where
  type FooBar Char = Char
  getFooFromBar = const 'a'

someFunction :: Bar b => b -> Int
someFunction = getInt . getFooFromBar
GHCi> someFunction 'z'
99 
duplode
  • 33,731
  • 7
  • 79
  • 150