由于Haskell的计算很懒,所以为什么此代码不起作用?

时间:2018-10-27 21:19:56

标签: haskell lazy-evaluation

--defining function
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just x

--calling function
safeHead (4:5:3:[]:[])

当我使用参数(4:5:3:[]:[])调用safeHead时,模式仅评估参数以查看其是否为空或是否具有头。因此,尽管没有意义的位[[]:[]”,它也不会抛出错误,因为这部分甚至都没有被评估。

3 个答案:

答案 0 :(得分:9)

  

它不会抛出错误,因为这部分甚至都没有评估。

不对其进行评估的事实无关紧要。 Haskell是一种静态类型的语言,编译器会检查类型。不必为了对值进行类型检查而计算结果:所有函数的输入和输出类型都是已知的(计算正确),Haskell编译器会验证一个函数的输出类型与的输入相同。用于处理该输出的函数。

类型检查在编译时完成,Haskell编译器不是惰性的(在某种意义上,它是在生成二进制文件之前而不是在运行之前执行这些检查的)代码)。编译器会急切地检查类型,并从类型系统的角度保证程序是明智的。

例如,以下表达式将进行类型检查:

1 : 2 : undefined

但是undefined :: a会在评估时引发错误。

Haskell允许定义Num bers的新类型,因此您可以创建一个将数字453解析为您自己类型的数字的类型类。严格来说,该类型可以是列表。

但是,如果您对此进行评估,Haskell将找不到要使用的类型,并且会引发错误:

Prelude> safeHead (4:5:3:[]:[])

<interactive>:6:1: error:
    • Non type-variable argument in the constraint: Num [t]
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall t. Num [t] => Maybe [t]

因此在这里,它正在寻找列表元素的类型,并且由于[]元素(最后一个元素),这些元素应该是列表,但同时这些应该是Num,现在找不到这种类型,因此会出错。

严格来说,我们可以构造这种类型,以便我们可以以适当的方式使用该功能:

Prelude> data A = A deriving Show
Prelude> :{
Prelude| instance Num [A] where
Prelude|     (+) = undefined
Prelude|     (*) = undefined
Prelude|     abs = undefined
Prelude|     fromInteger _ = [A]
Prelude|     negate = undefined
Prelude|     signum = undefined
Prelude| :}
Prelude> :{
Prelude| safeHead :: [a] -> Maybe a
Prelude| safeHead [] = Nothing
Prelude| safeHead (x:_) = Just x
Prelude| :}
Prelude> safeHead (4:5:3:[]:[]) :: Maybe [A]
Just [A]

答案 1 :(得分:3)

需要一些按摩,但是有时您可能会得到ghc来延缓诸如此类的 类型错误,并给出答案。

-- defer.hs
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just x

n :: Int
n = []

nonsense :: [Int]
nonsense = (4 : 5 : 3 : n : [])

r :: Maybe Int
r = safeHead nonsense

给出上述文件,如果您使用-fdefer-type-errors标志将其加载到ghc或ghci中...

$ ghci -fdefer-type-errors defer.hs

defer.hs:6:5: warning: [-Wdeferred-type-errors]
    • Couldn't match expected type ‘Int’ with actual type ‘[a0]’
    • In the expression: []
      In an equation for ‘n’: n = []
  |
6 | n = []
  |     ^^

ghci> r
Just 4

ghci> nonsense !! 3
*** Exception: defer.hs:6:5: error:
    • Couldn't match expected type ‘Int’ with actual type ‘[a0]’
    • In the expression: []
      In an equation for ‘n’: n = []
(deferred type error)

通常,Haskell程序的类型错误是致命的,从而阻止了它们的编译。但是, some 这样的错误可以推迟。这通常不是一个好主意,并且推迟类型错误的功能在可以推迟的程度上受到限制。

因此,为回答您的问题,Haskell并非旨在让类型错误滑动,即使该类型错误实际上并未影响任何实时代码路径。但是,您可以选择加入这种可疑的行为。

答案 2 :(得分:1)

Haskell程序中的每个位都必须具有类型。 预先

术语(4:5:3:[]:[])实际上具有类型。

> :t safeHead (4:5:3:[]:[])
safeHead (4:5:3:[]:[]) :: Num [t] => Maybe [t]

> safeHead (4:5:3:[]:[])

<interactive>:10:1:
    No instance for (Num [t0]) arising from a use of `it'
    In a stmt of an interactive GHCi command: print it

只是缺少实例。

> instance (Num a) => Num [a] where
    a + b = a
    a - b = a
    a * b = a
    abs a = a
    signum a = a
    fromInteger x = [fromInteger x]

> safeHead (4:5:3:[]:[])
Just [4]
it :: Num t => Maybe [t]

数字文字是多态定义,而不是具体值; 4实际上被读为(fromInteger 4) :: Num a => a,因此特定的fromInteger根据其实际具有的特定类型来执行其特定的工作。在这里

four :: Num t => Num [t]
four = fromInteger 4         -- according to our definition, it's
     = [fromInteger 4]

事实证明,这毕竟不是一个 complete 废话。 :)