Default type instances referring to each other - haskell

Is there a way to have default type instances defined in terms of each other? I'm trying to get something like this working:
{-# LANGUAGE DataKinds, KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
data Tag = A | B | C
class Foo (a :: *) where
type Bar a (b :: Tag)
type Bar a A = ()
type Bar a B = Bar a A
type Bar a C = Bar a A
instance Foo Int where
type Bar Int A = Bool
test :: Bar Int B
test = True
but this doesn't work:
Couldn't match type `Bar Int 'B' with `Bool'
In the expression: True
In an equation for `test': test = True
Note that this doesn't work either:
test :: Bar Int B
test = ()

Yes, default type instances can be defined in terms of each other (as you can see from your own example):
instance Foo Int where
-- So the default recursive definition will be used instead
-- type Bar Int A = Bool
test :: Bar Int B
test = ()
However when you redefine associated type synonym in your instance definition for Int you replace entire default 3-line defintion of Bar (and not just the type Bar a A = ()) with one line type Bar Int A = Bool which means Bar Int B and Bar Int C are no longer defined.
So I guess one of the ways to use recursive defaults the way you intended is to redefine specific synonyms instead (though it is rather verbose):
class Foo (a :: *) where
type Bar a (b :: Tag)
type Bar a A = BarA a
type Bar a B = BarB a
type BarA a
type BarA a = ()
type BarB a
type BarB a = Bar a A
-- This now works
instance Foo Int where
type BarA Int = Bool
test :: Bar Int B
test = True
Which can fall back to defaults:
-- As well as this one
instance Foo Int where
-- type BarA Int = Bool
test :: Bar Int B
test = ()

Related

Why is this type variable ambiguous, although it should be in scope?

Consider the following code:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
class Foo a where
type Bar a
class Foo a => Foo2 a where
bar :: Bar a
It gives the following error message in GHC 8.2:
error:
• Couldn't match expected type ‘Bar a’ with actual type ‘Bar a0’
NB: ‘Bar’ is a type function, and may not be injective
The type variable ‘a0’ is ambiguous
• In the ambiguity check for ‘bar’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method: bar :: forall a. Foo2 a => Bar a
In the class declaration for ‘Foo2’
|
7 | bar :: Bar a
| ^^^^^^^^^^^^
What's the problem? Why does it universally quantify over a? If I change the last line to
bar :: a
the problem vanishes. Why doesn't it have the type variable a in scope otherwise?
(I looked through all "ambiguous type variable" questions now, but nothing seems to help.)
Imagine you make two Foo instances with the same associated type, and then defined
..
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeApplications #-}
..
instance Foo Int where type Bar Int = Bool
instance Foo Float where type Bar Float = Bool
instance Foo2 Int where
bar :: Bool
bar = False
instance Foo2 Float where
bar :: Bool
bar = True
This means that the type of bar is not enough to decide between the Foo2 Int and Foo2 Float instances.
bar :: Foo2 a => Bar a
GHC will attempt to infer the type a for you but if you ask for
bar :: Bool
it has no way to pick between Int / Float or any other instance that may come later. You must explicitly specify the type with -XTypeApplications
>>> :set -XTypeApplications
>>
>> bar #Int
False
>> bar #Float
True
Edit: If every instance of Foo is a different type (Foo is injective) you can specify that the result determines the instance type with this syntax .. = res | res -> a
..
{-# Language TypeFamilyDependencies #-}
class Foo a where
type Bar a = res | res -> a
You can't define Bar Int and Bar Float both equal to Bool. If we only define Foo Int and Foo2 Int then bar :: Bool is enough to let GHC know you're looking for bar #Int = False.

Call a function inside a class without declaring an instance

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
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

How to make this example of pseudo-ducktyping type unambiguously without annotations

I wanted to demonstrate the idea of statically verifiable duck typing in Haskell using MultiParamTypeClasses, but I am having trouble avoiding type ambiguity.
Here is the code:
{-# LANGUAGE MultiParamTypeClasses #-}
class HasBar a b where
bar :: b -> a
data Foo = Foo { barBool :: Bool } deriving (Show)
instance HasBar Bool Foo where
bar = barBool
data Bazz = Bazz { barInt :: Int } deriving (Show)
instance HasBar Int Bazz where
bar = barInt
When I load it into GHCi and try to do bar (Foo True) or bar (Bazz 5) I get a Non type-variable argument error and it suggests FlexibleContexts, which just changes the error to an ambiguity error. Now doing something like False || bar (Foo True) works fine. But that doesn't seem like it should be needed as Foo is only a member of the typeclass that returns a Bool.
It seems like the issue is something to do with the possibility of something like:
instance HasBar Int Foo where
bar = const 5
Which would necessitate the types being ambiguous. But if there is just one instance I don't see why there are any issues preventing Haskell from finding out the type (do I need some sort of extension). If I can't do it that way then is there an alternative to MultiParamTypeClasses that only allows one instance and would allow for this pseudo-ducktyping type of thing to work?
the problem is that it's not only looking for what it sees but what it can know - and there is the possibility for you to make an instance that will be HasBar Int Foo as well so it complains
You can get rid of this with either FunctionalDependencies or TypeFamilies
using functional dependencies
the first extension is probably the way to go here (you don't have to change much of your code). You can basically tell GHCi, that the type b in your class/constraint will be enough to decide the type a.
if you change it to:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
class HasBar a b | b -> a where
bar :: b -> a
it'll work (you need the FlexibleContexts only in GHCi
λ> :set -XFlexibleContexts
λ> bar (Foo True)
True
using type families
In case you are interested here is the same thing with type-families and associated types:
{-# LANGUAGE TypeFamilies #-}
class HasBar a where
type Bar a :: *
bar :: a -> Bar a
data Foo = Foo { barBool :: Bool } deriving (Show)
instance HasBar Foo where
type Bar Foo = Bool
bar = barBool
data Bazz = Bazz { barInt :: Int } deriving (Show)
instance HasBar Bazz where
type Bar Bazz = Int
bar = barInt
note that you don't need the MultiParamTypeClasses any more

Type families: top level vs. associated

I am just starting to learn type families. The GHC documentation states that top level and associated type families have the same functionality, but the code I am writing behaves differently in top level than it does when the families are associated. This compiles and runs fine:
{-# LANGUAGE TypeFamilies #-}
module Test where
-- type family R a
-- type instance R Maybe = Int
class C' a where
type R a
getInt' :: a Int
getBool' :: R a -> a Bool
instance C' Maybe where
type R Maybe = Int
getInt' = Just 3
getBool' i = Just $ i < 10
printer :: IO ()
printer = print $ (getBool' 5 :: Maybe Bool)
but this gives me a type error:
{-# LANGUAGE TypeFamilies #-}
module Test where
type family R a
type instance R Maybe = Int
class C' a where
-- type R a
getInt' :: a Int
getBool' :: R a -> a Bool
instance C' Maybe where
-- type R Maybe = Int
getInt' = Just 3
getBool' i = Just $ i < 10
printer :: IO ()
printer = print $ (getBool' 5 :: Maybe Bool)
These look identical to me; why is it that one compiles and the other doesn't?
The second one works if you annotate the kind:
type family R (a :: * -> *)
I don't think there's any reason why the right kind is only inferred for the associated type family.

Inclusion of typeclasses with default implementation in Haskell

Consider the following definitions:
class Foo a where
foo :: a -> Int
class Bar a where
bar :: a -> [Int]
Now, how do I say "every Foo is also a Bar, with bar defined by default as bar x = [foo x]" in Haskell?
(Whatever I try, the compiler gives me "Illegal instance declaration" or "Constraint is no smaller than the instance head")
Btw, I can define my Foo and Bar classes in some other way, if this would help.
class Foo a where
foo :: a -> Int
-- 'a' belongs to 'Bar' only if it belongs to 'Foo' also
class Foo a => Bar a where
bar :: a -> [Int]
bar x = [foo x] -- yes, you can specify default implementation
instance Foo Char where
foo _ = 0
-- instance with default 'bar' implementation
instance Bar Char
As the automatic definition of a Bar instance through a Foo instance can lead to undecidable cases for the compiler - i.e. one explicit instance and one through Foo conflicting with each other - , we need some special options to allow the desired behaviour. The rest through is quite straigtforward.
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
class Foo a where
foo :: a -> Int
class Bar a where
bar :: a -> [Int]
instance (Foo a) => Bar a where
bar x = [foo x]
Generally speaking you don't model things with type classes this way[*] - i.e. an instance of a type class should always be some concrete type, though that type itself can be parameteric - e.g. the Show instance for pair has this signature:
instance (Show a, Show b) => Show (a,b) where
Some of the approaches to "Generics" allow you to model a general base case and then have type specific exceptional cases. SYB3 allowed this - perhaps unfortunately SYB3 isn't the common practice Generics library it is Data.Data / Data.Generics which I think is SYB1.
[*] In the wild the story is a little more complicated - as Dario says UndecidableInstances can enable it.

Resources