Haskell doesn't seem to recognize my type annotation below. Why is this?
Here Runner is a wrapper for a function, with a default starting value for c (a "continuant"). In rmap, I want c to have a default "starting" value (for example, if c were [a] I would let that value be []). What's of course inconvenient here (and perhaps this is bad practice, feel free to suggest a better way) is that a type annotation is required since the domain of rmap does not involve the type c. However, why can I not fix this by a type annotation?
data ExitCode = Fail | OK | Success deriving (Eq, Show)
data Runner a b c = Runner {cont ::c
, fun :: (a, c , ExitCode) ->(b, c, ExitCode)}
class Pointed a where
point :: a
rmap:: (Pointed c) => (a->b) -> Runner a b c
rmap f = Runner (point::c) (\(x,y,z) -> (f x,y,z))
The error is the following. (It seems to be interpreting c as c1.)
Could not deduce (Pointed c1) arising from a use of `point'
from the context (Pointed c)
bound by the type signature for
rmap :: Pointed c => (a -> b) -> Runner a b c
at Runner.hs:39:8-44
Possible fix:
add (Pointed c1) to the context of
an expression type signature: c1
or the type signature for
rmap :: Pointed c => (a -> b) -> Runner a b c
In the first argument of `Runner', namely `(point :: c)'
In the expression: Runner (point :: c) (\ (x, y, z) -> (f x, y, z))
In an equation for `rmap':
rmap f = Runner (point :: c) (\ (x, y, z) -> (f x, y, z))
We thus see that we make use of two functions here: (&&) :: Bool -> Bool -> Bool , and elem :: (Eq e, Foldable f) => e -> f e -> Bool , we here use e instead of f to avoid "name clashes" with our already defined type variable a .
Inbuilt Type Class In Haskell, every statement is considered as a mathematical expression and the category of this expression is called as a Type. You can say that "Type" is the data type of the expression used at compile time. To learn more about the Type, we will use the ":t" command.
The Eq typeclass provides an interface for testing for equality. Any type where it makes sense to test for equality between two values of that type should be a member of the Eq class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.
The Ord class is used for totally ordered datatypes. Instances of Ord can be derived for any user-defined datatype whose constituent types are in Ord. The declared order of the constructors in the data declaration determines the ordering in derived Ord instances.
In order to use type variables in a definition like that, you need the ScopedTypeVariables
language extension, but in this case you don't need the scoped type variable at all, just use
rmap :: Pointed c => (a -> b) -> Runner a b c
rmap f = Runner point (\(x, y, z) -> (f x, y, z))
If you really really want to have (point :: c)
, you can do
{-# LANGUAGE ScopedTypeVariables #-}
-- other declarations
rmap :: forall a b c. Pointed c => (a -> b) -> Runner a b c
rmap f = Runner (point :: c) (\(x, y, z) -> (f x, y, z))
With ScopedTypeVariables
you also have to explicitly use the forall
syntax and have a
and b
declared as well. Again, it isn't necessary for this particular problem, GHC can automatically figure out which instance of Pointed
to use.
Even with GHC 7.8, the type signature isn't even required, it can derive it automatically:
> let rmap f = Runner point (\(x, y, z) -> (f x, y, z))
> :t rmap
rmap :: Pointed c => (a -> b) -> Runner a b c
The origin of this error is that when you had (point :: c)
without ScopedTypeVariables
, the c
in the function definition is a different c
in the type signature. This works with concrete types like Int
because they're already in scope, but not so with type variables.
So as to why this works without having c
in the domain of the function: GHC's type inference is really smart. It'll let you pass around a generic value until the point that you require it to be concrete, then all the right instances get used. For example, if I instead had something like
> data Test a b c = Test { t :: c, x :: (a, b) } deriving (Eq, Show)
> instance Pointed [Double] where point = [1, 2, 3] -- Requires FlexibleInstances
> let test :: Pointed c => a -> b -> Test a b c
| test a b = Test point (a, b)
> test 1 "test" :: Test Int String [Double]
Test {t = [1.0,2.0,3.0], x = (1,"test")}
Even though c
doesn't appear as an argument, it's still possible for the type checker to figure out which instance to use when I've specified that the return type is Test Int String [Double]
. Without specifying the signature, it instead gives me the error
<interactive>:30:1:
No instance for (Pointed c0) arising from a use of `it'
The type variable `c0' is ambiguous
Note: there is a potential instance available:
instance Pointed [Double] -- Defined at <interactive>:19:10
In the first argument of `print', namely `it'
In a stmt of an interactive GHCi command: print it
because it doesn't know what instance of Pointed
to use.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With