函数中的Haskell类型注释

hol*_*lee 5 haskell

Haskell似乎没有识别我的类型注释.为什么是这样?

这里Runner是一个函数的包装器,具有c的默认起始值​​("连续").在rmap中,我希望c有一个默认的"起始"值(例如,如果c是[a],我会让该值为[]).这里当然不方便(也许这是不好的做法,随意提出更好的方法)是需要类型注释,因为rmap的域不涉及类型c.但是,为什么我不能通过类型注释来解决这个问题呢?

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))
Run Code Online (Sandbox Code Playgroud)

错误如下.(似乎将c解释为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))
Run Code Online (Sandbox Code Playgroud)

bhe*_*ilr 9

为了在类似的定义中使用类型变量,您需要ScopedTypeVariables语言扩展,但在这种情况下,您根本不需要范围类型变量,只需使用

rmap :: Pointed c => (a -> b) -> Runner a b c
rmap f = Runner point (\(x, y, z) -> (f x, y, z))
Run Code Online (Sandbox Code Playgroud)

如果你真的想拥有(point :: c),你可以做到

{-# 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))
Run Code Online (Sandbox Code Playgroud)

随着ScopedTypeVariables你也必须明确使用forall语法和具有ab申报为好.同样,对于这个特定问题没有必要,GHC可以自动确定Pointed使用哪个实例.

即使使用GHC 7.8,甚至不需要类型签名,它也可以自动派生:

> let rmap f = Runner point (\(x, y, z) -> (f x, y, z))
> :t rmap
rmap :: Pointed c => (a -> b) -> Runner a b c
Run Code Online (Sandbox Code Playgroud)

此错误的原因是,当您(point :: c)没有时ScopedTypeVariables,c函数定义c中的类型签名是不同的.这适用于具体类型,例如Int因为它们已经在范围内,但对于类型变量则不然.


因此,为什么这个工作没有c在函数的范围内:GHC的类型推断非常聪明.它会让你传递一个通用值,直到你要求它具体化,然后使用所有正确的实例.例如,如果我有类似的东西

> 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")}
Run Code Online (Sandbox Code Playgroud)

即使c不作为参数出现,类型检查器仍然可以确定在我指定返回类型时使用哪个实例Test Int String [Double].没有指定签名,它反而给我错误

<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
Run Code Online (Sandbox Code Playgroud)

因为它不知道使用什么实例Pointed.