Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell type annotation in function

Tags:

haskell

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))
like image 572
holdenlee Avatar asked Aug 18 '14 17:08

holdenlee


People also ask

Which function is used to find type signature in Haskell?

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 .

What type is type Haskell?

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.

What is Eq type in Haskell?

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.

How does ord work in Haskell?

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.


1 Answers

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.

like image 184
bheklilr Avatar answered Sep 20 '22 02:09

bheklilr