理解Haskell Type-Ambiguity的案例

rec*_*nja 5 haskell ambiguity ghc

我写了一个Haskell程序并得到了一个我不明白的编译错误.

该计划应:

  • 获取命令行参数
  • 将标记化参数连接回单个参数 String
  • StringNestedList数据类型
  • 将其压平NestedList成一个List
  • 打印 List

不幸的是,由于类型模糊,它不会编译.

Haskell代码:

{-
  Run like this:
  $ ./prog List [Elem 1, List [Elem 2, List [Elem 3, Elem 4], Elem 5]]
  Output: [1,2,3,4,5]
-}
import System.Environment
import Data.List

data NestedList a = Elem a | List [NestedList a]
  deriving (Read)

main = do
  args <- getArgs
  print . flatten . read $ intercalate " " args

flatten :: NestedList a -> [a]
flatten (Elem x) = [x]
flatten (List x) = concatMap flatten x
Run Code Online (Sandbox Code Playgroud)

编译错误:

prog.hs:8:21:
    Ambiguous type variable `a0' in the constraints:
      (Read a0) arising from a use of `read' at prog.hs:8:21-24
      (Show a0) arising from a use of `print' at prog.hs:8:3-7
    Probable fix: add a type signature that fixes these type variable(s)
    In the second argument of `(.)', namely `read'
    In the second argument of `(.)', namely `flatten . read'
    In the expression: print . flatten . read
Run Code Online (Sandbox Code Playgroud)

有人可以帮助我理解如何/为什么存在类型模糊以及如何使代码明确无误.

J. *_*son 7

每当类型变量因函数应用而消失时,Haskell中就会出现不明确的类型.你在这里的那个read/show很常见.这是问题所在:

让我们尝试读取一个字符串,这个操作有类型

read :: Read a => String -> a
Run Code Online (Sandbox Code Playgroud)

这样,如果我们给它一个字符串,我们只会得到一个类似的类型

read "()" :: Read a => a
Run Code Online (Sandbox Code Playgroud)

换句话说,类型系统还没有能够选择具体的类型 - 它只是知道无论答案是什么,它必须Read能够.

问题是,如果我们回头并立即显示,我们正在应用一个函数

show :: Show a => a -> String
Run Code Online (Sandbox Code Playgroud)

这也没有完全指定类型a.结合它们给了我们

show (read "()") :: String
Run Code Online (Sandbox Code Playgroud)

我们失去了决定这种中间类型应该是什么的所有机会.

由于这种模糊性,Haskell不允许这样的表达.你通过某种方式插入一个完全约束类型的函数来修复它.一种常见的方法是使用该功能asTypeOf

asTypeOf :: a -> a -> a
asTypeOf = const
Run Code Online (Sandbox Code Playgroud)

这确保了第一个和第二个参数具有相同的类型.

> show (read "()" `asTypeOf` ()) :: String
"()"
Run Code Online (Sandbox Code Playgroud)

在您的特定例子中,你需要确定什么aNestedList a.一种简单的方法是明确地将类型flatten作为具体类型.

print . (flatten :: NestedList Int -> [Int]) . read $ concat args
Run Code Online (Sandbox Code Playgroud)