Calling a function inside a class without declaring an instance

I want to create a function in Haskell that does different things depending on the datatype it sets. I thought that classes should do what I want, but now I have a problem. What I would like 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

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, "A variable of type a0 is ambiguous in the ambiguity check for 'myFunction', and from what I've read, it has to do with the compiler not knowing which instance of 'doSomething' to call.

So, is there a way to call "doSomething" in a "generic" way and force the datatype later? Or do I need a completely different approach to my problem?

--- EDIT ---

So, I applied chi's answer to my problem, but it hasn't completely solved it 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'm at the prompt

create @ Bool 4
create @ Int 4

      

returns expected results. However, anotherFunction does not compile correctly, giving an 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:10In the expression: doSomething $ create 4
      In an equation for ‘anotherFunction’:
          anotherFunction x = doSomething $ create 4
Failed, modules loaded: none.

      

Is it just not possible to use doSomething in this context? My idea is to implement the function in the same way for all instances and then write

anotherFunction @ Bool 4
anotherFunction @ Int 6

      

+3


source to share


1 answer


You need multiple extensions to do this, but it's 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

      

the "old" solution would be to add a proxy argument

class C a where myFunction :: proxy a -> Int

      

but hopefully this fades away due to style over the years - I find missing types clearly clearer than proxy passing.




Complete 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

      

+4


source







All Articles