使用多参数类型类的不明确的类型变量

时间:2012-10-09 10:24:47

标签: haskell higher-rank-types

我不明白为什么Haskell无法在下面的代码中找出第8行的类型。 expressMaybe函数的类型签名是否确定结果类型与两个输入参数的类型相同?

{-# LANGUAGE MultiParamTypeClasses #-}

class Gene g n where
  express :: g -> g -> g
  -- there will be other functions that use the "n" type parameter

expressMaybe :: Gene g n => Maybe g -> Maybe g -> Maybe g
expressMaybe (Just a) (Just b) = Just (express a b) -- line 8
expressMaybe (Just a) Nothing  = Just a
expressMaybe Nothing (Just b)  = Just b
expressMaybe Nothing Nothing   = Nothing

我得到的错误是:

Amy20.hs:8:40:
    Ambiguous type variable `n0' in the constraint:
      (Gene g n0) arising from a use of `express'
    Probable fix: add a type signature that fixes these type variable(s)
    In the first argument of `Just', namely `(express a b)'
    In the expression: Just (express a b)
    In an equation for `expressMaybe':
        expressMaybe (Just a) (Just b) = Just (express a b)
Failed, modules loaded: none.

我尝试使用RankNTypes和ScopedTypeVariables,但我无法弄清楚如何使错误消失。

提前感谢您的帮助!

编辑:现在我理解了这个问题,我使用了fundeps,因为我对它们很熟悉,对于我的应用程序来说,对于编码基因有一个以上的“字母表”没有多大意义。我以前从未使用过类型系列,所以我也会研究它。

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class Gene g n | g -> n where
  express :: g -> g -> g
  -- there will be other functions that use the "n" type parameter

expressMaybe :: Gene g n => Maybe g -> Maybe g -> Maybe g
expressMaybe (Just a) (Just b) = Just (express a b) -- line 8
expressMaybe (Just a) Nothing  = Just a
expressMaybe Nothing (Just b)  = Just b
expressMaybe Nothing Nothing   = Nothing

2 个答案:

答案 0 :(得分:12)

考虑一下:

instance Gene String Int where
  express _ _ = "INT"

instance Gene String Double where
  express _ _ = "DOUBLE"

expressMaybe (Just "") (Just "")

该代码应该生成(Just "INT")还是(Just "DOUBLE")?是的,Haskell知道expressMaybe的结果与参数的类型相同。但这并不意味着它知道在这里使用哪个实例,因为对于具有不同g的相同类型n,可能存在多个实例。

如果在您的情况下,每种类型n只会有一种类型g,您可以考虑使用类型系列或函数依赖项等扩展,您可以使用这些类型向类型系统表达该事实

答案 1 :(得分:1)

我建议你避免功能依赖,并选择类型系列。它们更有趣,更直观:

{-# LANGUAGE TypeFamilies, KindSignatures #-}

class Gene g where
  type Nucleotide g :: *  -- each instance has an associated type
  express :: g -> g -> g
  encode :: [Nucleotide g] -> g -- probably doesn't make sense, but need an example
  -- there will be other functions that use the "Nucleotide g" type parameter

假设我们有

data ACTG = A | C | T | G
data ACTGgene = ACTGgene [ACTG]

基本上,您可以通过类型级别的简单模式匹配来定义类型函数:

instance Gene ACTGgene where
  type Nucleotide ACTGgene = ACTG
  encode ns = ACTGgene ns
  express = error "I'm out of my depth here because I gave up Biology when I hit 14."

现在我们的代码编译:

expressMaybe :: Gene g => Maybe g -> Maybe g -> Maybe g
expressMaybe (Just a) (Just b) = Just (express a b) -- compiles fine
expressMaybe (Just a) Nothing  = Just a
expressMaybe Nothing (Just b)  = Just b
expressMaybe Nothing Nothing   = Nothing

现在可能是你的基因类型基本上取决于你正在使用的核苷酸,或者 也许你可以互换使用不同的核苷酸,我不知道,但一个可能的解决方案 你的问题是改为使用构造函数类:

class Gene g where
   express :: g n -> g n -> g n
   encode :: n -> g n
   -- more stuff

这适用于像

这样的数据声明
data ACTG = A | C | T | G
data ListGene n = ListGene [n]

instance Gene ListGene where
  ...

但也许这对你的问题领域来说过于参与,我不知道。


当然你应该尝试打字。我做到了。 Now I love them. - 链接的答案包括最后的链接以供进一步阅读。