拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Haskell:型别变量对于类实体不明确

Haskell:型别变量对于类实体不明确

白鹭 - 2022-01-25 2100 0 0
import qualified Prelude

class List list where
  toList :: list a -> [a]
  fromList :: [a] -> list a

data Deque a = Deque [a] [a]

instance List Deque where
  toList (Deque xs sy) = xs    reverse sy

  fromList xs = Deque ys (reverse zs)
    where (ys, zs) = splitAt (length xs `div` 2) xs

我收到一个错误,如下所示。GHCI 似乎没有检测ys为 Deque,而是作为 List 类的一般实体。

ghci> xs = [2, 3, 4]
ghci> ys = fromList xs
ghci> (Deque a b) = ys
ghci> toList (Deque a b)
[2,3,4]
ghci> toList ys

<interactive>:5:1: error:
    * Could not deduce (List list0) arising from a use of `toList'
      from the context: Num a
        bound by the inferred type of it :: Num a => [a]
        at <interactive>:5:1-9
      The type variable `list0' is ambiguous
      These potential instance exist:
        instance [safe] List Deque -- Defined at main.hs:12:10
    * In the expression: toList ys
      In an equation for `it': it = toList ys

uj5u.com热心网友回复:

ghci> let xs = [2, 3, 4]
ghci> let ys = fromList ys

此时,这些值可能具有最通用的类??型:

ghci> :t xs
xs :: Num a => [a]
ghci> :t ys
ys :: (List list, Num a) => list a

没有暗示 a Deque,这是有道理的,因为您还没有提到任何双端队列。

但是这些变量保持不变,它们不会改变它们的型别!

所以之后

ghci> (Deque a b) = ys
ghci> toList (Deque a b)
[2,3,4]

你仍然有同样的情况ys,即

ghci> :t ys
ys :: (List list, Num a) => list a

当然,您曾经用作ys双端队列,这是可以使用的一种方式。但由于是多型的,它也可以是任何其他List型别,GHCi 不会做出任何猜测它应该是什么。但你当然可以告诉它

ghci> toList (ys :: Deque Int)
[2,3,4]

或者更短

ghci> :set -XTypeApplications 
ghci> toList @Deque ys
[2,3,4]

uj5u.com热心网友回复:

这是按设计作业的。当你写:

λ> ys = fromList xs

分配给的型别ys是:

λ> :t ys
ys :: (List list, Prelude.Num a) => list a

也就是说,ys是一个多型值,可以“是”任何型别的List.

因此,当您将其系结到特定模式时:

λ> Deque a b = ys

thenys被实体化为 aDeque并且适当的值系结到aand b,但这不会使ys单态。也就是说,ys还没有突然“变成”一个Deque. 相反,它仍然是一个多型值,可以反弹到其他一些List. 例如,如果您还有一个普通旧串列的实体:

instance List [] where
  toList = id
  fromList = id

您可以实体化ys多种型别:

λ> xs = [2, 3, 4]
λ> ys = fromList xs
λ> Deque a b = ys     -- instantiate as Deque
λ> c:d = ys           -- instantiate as []

如果这让您感到奇怪,请考虑:

λ> id "hello"
λ> id 'x'

两者都有效,即使第一次使用多型值id :: a -> a,它也被实体化为String -> String,而第二次使用时,它被实体化为Char -> Char. 这是相同的原理。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *