Haskell 获取代数参数的类型

cro*_*eea 4 haskell pattern-matching

我有一个类型

class IntegerAsType a where
  value :: a -> Integer

data T5
instance IntegerAsType T5 where value _ = 5

newtype (IntegerAsType q) => Zq q = Zq Integer deriving (Eq)

newtype (Num a, IntegerAsType n) => PolyRing a n = PolyRing [a]
Run Code Online (Sandbox Code Playgroud)

我正在尝试为 PolyRing 类型制作一个不错的“表演”。特别是,我希望“show”打印出类型“a”。是否有返回代数参数类型的函数(类型的“显示”)?

我尝试使用的另一种方法是使用模式匹配,但我遇到了内置类型和代数类型的问题。

我想要 Integer、Int 和 Zq q 中的每一个都有不同的结果。(玩具示例:)

test :: (Num a, IntegerAsType q) => a -> a
(Int x) = x+1
(Integer x) = x+2
(Zq x) = x+3
Run Code Online (Sandbox Code Playgroud)

这里至少有两个不同的问题。

1) Int 和 Integer 不是 'Int' 和 'Integer' 类型的数据构造函数。是否有这些类型的数据构造函数/我如何与它们进行模式匹配?

2)虽然没有在我的代码中显示,Zq 是 Num 的一个实例。我遇到的问题是:

Ambiguous constraint `IntegerAsType q'
At least one of the forall'd type variables mentioned by the constraint 
must be reachable from the type after the '=>'
In the type signature for `test':
test :: (Num a, IntegerAsType q) => a -> a
Run Code Online (Sandbox Code Playgroud)

我有点明白它为什么抱怨,但我不知道如何解决这个问题。

谢谢

编辑:我试图用测试功能做的一个更好的例子:

test :: (Num a) => a -> a
test (Integer x) = x+2
test (Int x) = x+1
test (Zq x) = x
Run Code Online (Sandbox Code Playgroud)

即使我们忽略了我不能以这种方式构造整数和整数的事实(仍然想知道如何!)这个“测试”也不会编译,因为:

Could not deduce (a ~ Zq t0) from the context (Num a)
Run Code Online (Sandbox Code Playgroud)

我对这个函数的下一次尝试是使用类型签名:

test :: (Num a, IntegerAsType q) => a -> a
Run Code Online (Sandbox Code Playgroud)

这导致了新的错误

Ambiguous constraint `IntegerAsType q'
At least one of the forall'd type variables mentioned by the constraint 
must be reachable from the type after the '=>'
Run Code Online (Sandbox Code Playgroud)

我希望这能让我的问题更清楚一点......

Dan*_*ner 5

我不确定您使用该test功能的目的是什么,但是如果您愿意,可以执行以下操作:

{-# LANGUAGE ScopedTypeVariables #-}
class NamedType a where
    name :: a -> String
instance NamedType Int where
    name _ = "Int"
instance NamedType Integer where
    name _ = "Integer"
instance NamedType q => NamedType (Zq q) where
    name _ = "Zq (" ++ name (undefined :: q) ++ ")"
Run Code Online (Sandbox Code Playgroud)

如果我没有用警告来跟进这个答案,我就不会履行我的 Stack Overflow 职责:你所要求的非常非常奇怪。您可能正在以一种非常单调的方式做某事,并且将在整个过程中与语言作斗争。我强烈建议您的下一个问题是一个更广泛的设计问题,以便我们可以帮助指导您找到更惯用的解决方案。

编辑

您的问题还有另一半,即如何test在输入上编写一个“模式匹配”的函数来检查它是否是 an Int、 an Integer、 aZq类型等。您提供了以下提示性代码片段:

test :: (Num a) => a -> a
test (Integer x) = x+2
test (Int x) = x+1
test (Zq x) = x
Run Code Online (Sandbox Code Playgroud)

这里有几件事需要澄清。

Haskell 具有三个级别的对象:值级别、类型级别和种类级别。值级别事物的一些示例包括"Hello, world!"42、 函数\a -> afix (\xs -> 0:1:zipWith (+) xs (tail xs))。在类型级别的东西的一些例子包括BoolIntMaybeMaybe Int,和Monad m => m ()。种类级别的一些示例包括*(* -> *) -> *

级别是有序的;值级对象按类型级对象分类,类型级对象按种类级对象分类。我们使用 来编写分类关系::,例如,32 :: Int"Hello, world!" :: [Char]。(本次讨论的种类级别不是太有趣,但*对类型进行分类,箭头种类对类型构造函数进行分类。例如,Int :: *[Int] :: *,但是[] :: * -> *。)

现在,Haskell 最基本的特性之一是每个级别都是完全隔离的。你永远不会"Hello, world!"在类型中看到字符串;类似地,值级对象不会传递或操作类型。此外,值和类型有单独的命名空间。举个例子Maybe

data Maybe a = Nothing | Just a
Run Code Online (Sandbox Code Playgroud)

此声明Maybe :: * -> *在类型级别创建了一个新名称,Nothing :: Maybe aJust :: a -> Maybe a在值级别创建了两个新名称。一种常见的模式是对类型构造函数和它的值构造函数使用相同的名称(如果只有一个);例如,你可能会看到

newtype Wrapped a = Wrapped a
Run Code Online (Sandbox Code Playgroud)

Wrapped :: * -> *在类型级别声明了一个新名称,同时Wrapped :: a -> Wrapped a在值级别声明了一个不同的名称。一些特别常见(和令人困惑的例子)包括(),它既是值级对象(类型())又是类型级对象(种类*),并且[],它既是值级对象(类型[a])又是类型级对象(种类* -> *)。请注意,值级别和类型级别对象在您的源中碰巧拼写相同的事实只是巧合!如果你想迷惑你的读者,你完全可以写

newtype Huey  a = Louie a
newtype Louie a = Dewey a
newtype Dewey a = Huey  a
Run Code Online (Sandbox Code Playgroud)

这三个声明中没有一个相互关联!

现在,我们终于可以解决test上面的问题:Integer并且Int不是值构造函数,因此它们不能在模式中使用。请记住——值级别和类型级别是隔离的,因此您不能将类型名称放在值定义中!到现在为止,您可能希望test'改为编写:

test' :: Num a => a -> a
test' (x :: Integer) = x + 2
test' (x :: Int) = x + 1
test' (Zq x :: Zq a) = x
Run Code Online (Sandbox Code Playgroud)

...但唉,它并不完全像那样工作。不允许值级别的东西依赖于类型级别的东西。你可以做的是编写单独的每个功能IntIntegerZq a类型:

testInteger :: Integer -> Integer
testInteger x = x + 2

testInt :: Int -> Int
testInt x = x + 1

testZq :: Num a => Zq a -> Zq a
testZq (Zq x) = Zq x
Run Code Online (Sandbox Code Playgroud)

然后我们可以在要进行测试时调用这些函数中的适当一个。由于我们使用的是静态类型语言,因此这些函数中的一个将适用于任何特定变量。

现在,记住调用正确的函数有点麻烦,所以 Haskell 提供了一点便利:您可以让编译器在编译时为您选择这些函数之一。这种机制是类背后的重要思想。它看起来像这样:

class Testable a where test :: a -> a
instance Testable Integer where test = testInteger
instance Testable Int where test = testInt
instance Num a => Testable (Zq a) where test = testZq
Run Code Online (Sandbox Code Playgroud)

现在,它看起来像有称为单一功能test,可以处理任何的IntInteger或数字Zq的-但实际上有三个功能,并且编译器会透明地选择一个给你。这是一个重要的见解。的类型test

test :: Testable a => a -> a
Run Code Online (Sandbox Code Playgroud)

...乍一看好像它是一个函数,它接受一个可以是任何Testable类型的值。但事实上,它是一个可以专门用于任何Testable类型的函数——然后接受该类型的值!这种差异解释了原始test功能不起作用的另一个原因。不能有多个包含不同类型变量的模式,因为该函数一次只能作用于一种类型。

NamedTypeTestable以上类背后的想法可以概括一下;如果你这样做了,你就会得到Typeable上面 hammar 建议的课程。

我想现在我已经说得够多了,而且可能混淆的东西比我澄清的要多,但是请给我留言,说明哪些部分不清楚,我会尽力而为。