Reputation: 164
I want to create a function in Haskell that does different things depending the data type it is given. I thought classes should do what I want, but now I ran into a problem. What I would like to be able to do is something similar to:
let x1 = myFunction :: MyInstance1
let x2 = myFunction :: MyInstance2
and it does different things depending on the given instance.
My current approach is
class MyClass a where
create :: Int -> a
doSomething :: a -> [Int]
myFunction :: [Int]
myFunction = doSomething $ create 4
instance MyClass MyInstance1 where
-- implementation of create and doSomething
instance MyClass MyInstance2 where
-- implementation of create and doSomething
However, the compiler tells me "The type variable a0 is ambiguous in the ambiguity check for 'myFunction'" and from what I've been reading this is related to the compiler not knowing what instance of 'doSomething' to call.
So is there a way to call 'doSomething' in a "generic" way and enforce the data type later? Or do I need an entirely different approach for my problem?
--- EDIT ---
So I applied chi's answer to my problem, but it does not solve it completely yet. Here's my code
{-# LANGUAGE AllowAmbiguousTypes #-}
class C a where
myFunction :: Int
create :: Int -> a
doSomething :: a -> Int
-- anotherFunction :: Int -> Int
-- anotherFunction x = doSomething $ create 4
instance C Int where
myFunction = 1
create x = 2 * x
doSomething x = x + 4
instance C Bool where
myFunction = 2
create x = True
doSomething x = if x then 42 else 24
This compiles and I in the prompt
create @ Bool 4
create @ Int 4
returns the expected results. However, anotherFunction does not compile properly giving the error message
Test.hs:8:23: error:
• Could not deduce (C a0) arising from a use of ‘doSomething’
from the context: C a
bound by the class declaration for ‘C’ at Test.hs:(3,1)-(8,44)
The type variable ‘a0’ is ambiguous
These potential instances exist:
instance C Bool -- Defined at Test.hs:15:10
instance C Int -- Defined at Test.hs:10:10
• In the expression: doSomething $ create 4
In an equation for ‘anotherFunction’:
anotherFunction x = doSomething $ create 4
Failed, modules loaded: none.
Is it simply not possible to use doSomething in this context? My idea is to implement the function in the same manner for all instances and then write
anotherFunction @ Bool 4
anotherFunction @ Int 6
Upvotes: 3
Views: 438
Reputation: 116139
You need a couple of extensions to do that, but it is doable. Here's a GHCi session showing that:
> :set -XAllowAmbiguousTypes
> class C a where myFunction :: Int
> instance C Int where myFunction = 1
> instance C Bool where myFunction = 2
> :set -XTypeApplications
> myFunction @ Int
1
> myFunction @ Bool
2
An "old" solution would be to add a proxy argument
class C a where myFunction :: proxy a -> Int
but hopefully this will fade out of style in a few years -- I find passing types explicitly clearer than passing proxies.
Full code, with another example:
{-# LANGUAGE AllowAmbiguousTypes, TypeApplications, ScopedTypeVariables #-}
class C a where
myFunction :: Int
create :: Int -> a
doSomething :: a -> Int
anotherFunction :: Int -> Int
anotherFunction x = doSomething $ create @ a 4
instance C Int where
myFunction = 1
create x = 2 * x
doSomething x = x + 4
instance C Bool where
myFunction = 2
create x = True
doSomething x = if x then 42 else 24
Tests:
> :set -XTypeApplications
> anotherFunction @ Bool 4
42
> anotherFunction @ Int 6
12
Upvotes: 4