简体   繁体   English

Haskell得到代数参数的类型

[英]Haskell get type of algebraic parameter

I have a type 我有一个类型

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]

I'm trying to make a nice "show" for the PolyRing type. 我正试图为PolyRing类型做一个很好的“show”。 In particular, I want the "show" to print out the type 'a'. 特别是,我希望“show”打印出类型 'a'。 Is there a function that returns the type of an algebraic parameter (a 'show' for types)? 是否有一个返回代数参数类型的函数(类型的'show')?

The other way I'm trying to do it is using pattern matching, but I'm running into problems with built-in types and the algebraic type. 我尝试这样做的另一种方法是使用模式匹配,但我遇到了内置类型和代数类型的问题。

I want a different result for each of Integer, Int and Zq q. 我希望每个Integer,Int和Zq q都有不同的结果。 (toy example:) (玩具示例:)

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

There are at least two different problems here. 这里至少有两个不同的问题。

1) Int and Integer are not data constructors for the 'Int' and 'Integer' types. 1)Int和Integer不是'Int'和'Integer'类型的数据构造函数。 Are there data constructors for these types/how do I pattern match with them? 是否存在这些类型的数据构造函数/如何与它们进行模式匹配?

2) Although not shown in my code, Zq IS an instance of Num. 2)虽然我的代码中没有显示,但Zq是Num的一个实例。 The problem I'm getting is: 我得到的问题是:

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

I kind of see why it is complaining, but I don't know how to get around that. 我有点看到它为什么抱怨,但我不知道如何解决这个问题。

Thanks 谢谢

EDIT: A better example of what I'm trying to do with the test function: 编辑:我正在尝试使用测试功能的一个更好的例子:

test :: (Num a) => a -> a
test (Integer x) = x+2
test (Int x) = x+1
test (Zq x) = x

Even if we ignore the fact that I can't construct Integers and Ints this way (still want to know how!) this 'test' doesn't compile because: 即使我们忽略了这样一个事实:我无法以这种方式构造整数和Ints(仍然想知道如何!)这个'test'不能编译,因为:

Could not deduce (a ~ Zq t0) from the context (Num a)

My next try at this function was with the type signature: 我对此函数的下一次尝试是使用类型签名:

test :: (Num a, IntegerAsType q) => a -> a

which leads to the new error 这导致了新的错误

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 '=>'

I hope that makes my question a little clearer.... 我希望这会让我的问题更加清晰......

I'm not sure what you're driving at with that test function, but you can do something like this if you like: 我不确定你使用该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) ++ ")"

I would not be doing my Stack Overflow duty if I did not follow up this answer with a warning: what you are asking for is very, very strange. 如果我没有通过警告跟进这个答案,我不会做我的Stack Overflow任务:你要求的是非常非常奇怪的。 You are probably doing something in a very unidiomatic way, and will be fighting the language the whole way. 你可能正在以一种非常单一的方式做一些事情,并将全力以赴地对抗语言。 I strongly recommend that your next question be a much broader design question, so that we can help guide you to a more idiomatic solution. 我强烈建议您的下一个问题是一个更广泛的设计问题,以便我们可以帮助您指导更加惯用的解决方案。

Edit 编辑

There is another half to your question, namely, how to write a test function that "pattern matches" on the input to check whether it's an Int , an Integer , a Zq type, etc. You provide this suggestive code snippet: 您的问题还有另外一半,即如何在输入上编写“模式匹配”的test函数,以检查它是IntIntegerZq类型等。您提供了这个暗示性代码片段:

test :: (Num a) => a -> a
test (Integer x) = x+2
test (Int x) = x+1
test (Zq x) = x

There are a couple of things to clear up here. 这里有几件事需要澄清。

Haskell has three levels of objects: the value level, the type level, and the kind level. Haskell有三个级别的对象:值级别,类型级别和种类级别。 Some examples of things at the value level include "Hello, world!" 价值层面的一些事例包括"Hello, world!" , 42 , the function \\a -> a , or fix (\\xs -> 0:1:zipWith (+) xs (tail xs)) . 42 ,函数\\a -> a ,或者fix (\\xs -> 0:1:zipWith (+) xs (tail xs)) Some examples of things at the type level include Bool , Int , Maybe , Maybe Int , and Monad m => m () . 类型级别的一些示例包括BoolIntMaybeMaybe IntMonad m => m () Some examples of things at the kind level include * and (* -> *) -> * . 类型级别的一些示例包括*(* -> *) -> *

The levels are in order; 水平是有序的; value level objects are classified by type level objects, and type level objects are classified by kind level objects. 值级别对象按类型级别对象分类,类型级别对象按类型级别对象分类。 We write the classification relationship using :: , so for example, 32 :: Int or "Hello, world!" :: [Char] 我们使用::编写分类关系,例如, 32 :: Int"Hello, world!" :: [Char] "Hello, world!" :: [Char] . "Hello, world!" :: [Char] (The kind level isn't too interesting for this discussion, but * classifies types, and arrow kinds classify type constructors. For example, Int :: * and [Int] :: * , but [] :: * -> * .) (对于此讨论,类型级别并不太有趣,但*对类型进行分类,箭头类型对类型构造函数进行分类。例如, Int :: *[Int] :: * ,但是[] :: * -> * 。 )

Now, one of the most basic properties of Haskell is that each level is completely isolated. 现在,Haskell最基本的属性之一是每个级别都是完全隔离的。 You will never see a string like "Hello, world!" 你永远不会看到像"Hello, world!"这样的字符串"Hello, world!" in a type; 在一个类型; similarly, value-level objects don't pass around or operate on types. 类似地,值级对象不会传递或操作类型。 Moreover, there are separate namespaces for values and types. 此外,值和类型有单独的命名空间 Take the example of Maybe : Maybe为例:

data Maybe a = Nothing | Just a

This declaration creates a new name Maybe :: * -> * at the type level, and two new names Nothing :: Maybe a and Just :: a -> Maybe a at the value level. 该声明在类型级别创建一个新名称Maybe :: * -> * ,并且在值级别创建两个新名称Nothing :: Maybe aJust :: a -> Maybe a One common pattern is to use the same name for a type constructor and for its value constructor, if there's only one; 一种常见的模式是对类型构造函数和它的值构造函数使用相同的名称,如果只有一个; for example, you might see 例如,您可能会看到

newtype Wrapped a = Wrapped a

which declares a new name Wrapped :: * -> * at the type level, and simultaneously declares a distinct name Wrapped :: a -> Wrapped a at the value level. 它在类型级别声明一个新名称Wrapped :: * -> * ,同时声明一个不同的名称Wrapped :: a -> Wrapped a在值级别。 Some particularly common (and confusing examples) include () , which is both a value-level object (of type () ) and a type-level object (of kind * ), and [] , which is both a value-level object (of type [a] ) and a type-level object (of kind * -> * ). 一些特别常见(和令人困惑的例子)include () ,它既是值级对象(类型() ),也是类型级对象(类型* )和[] ,它们都是值级对象(类型[a] )和类型级对象(类型* -> * )。 Note that the fact that the value-level and type-level objects happen to be spelled the same in your source is just a coincidence! 请注意,值源级别和类型级别对象恰好在源代码中拼写相同这一事实只是巧合! If you wanted to confuse your readers, you could perfectly well write 如果你想让读者感到困惑,你可以写得很好

newtype Huey  a = Louie a
newtype Louie a = Dewey a
newtype Dewey a = Huey  a

where none of these three declarations are related to each other at all! 这三个声明中没有一个完全相互关联!

Now, we can finally tackle what goes wrong with test above: Integer and Int are not value constructors, so they can't be used in patterns. 现在,我们可以最终解决上面的test什么问题: IntegerInt不是值构造函数,因此它们不能用于模式。 Remember -- the value level and type level are isolated, so you can't put type names in value definitions! 请记住 - 值级别和类型级别是隔离的,因此您不能将类型名称放在值定义中! By now, you might wish you had written test' instead: 现在,你可能会希望你有写test' ,而不是:

test' :: Num a => a -> a
test' (x :: Integer) = x + 2
test' (x :: Int) = x + 1
test' (Zq x :: Zq a) = x

...but alas, it doesn't quite work like that. ......但是,唉,它并不像那样。 Value-level things aren't allowed to depend on type-level things. 不允许价值级别的东西依赖于类型级别的东西。 What you can do is to write separate functions at each of the Int , Integer , and Zq a types: 可以做的是在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

Then we can call the appropriate one of these functions when we want to do a test. 然后,当我们想要进行测试时,我们可以调用这些函数中的相应函数。 Since we're in a statically-typed language, exactly one of these functions is going to be applicable to any particular variable. 由于我们使用的是静态类型语言,因此这些函数中的任何一个都适用于任何特定变量。

Now, it's a bit onerous to remember to call the right function, so Haskell offers a slight convenience: you can let the compiler choose one of these functions for you at compile time. 现在,记住调用正确的函数有点繁琐,因此Haskell提供了一些方便:您可以让编译器在编译时为您选择其中一个函数。 This mechanism is the big idea behind classes. 这种机制是课程背后的重要思想。 It looks like this: 它看起来像这样:

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

Now, it looks like there's a single function called test which can handle any of Int , Integer , or numeric Zq 's -- but in fact there are three functions, and the compiler is transparently choosing one for you. 现在, 看起来有一个名为test函数可以处理IntIntegerZq的任何一个 - 但实际上有三个函数,编译器会透明地为你选择一个函数。 And that's an important insight. 这是一个重要的见解。 The type of test : test类型:

test :: Testable a => a -> a

...looks at first blush like it is a function that takes a value that could be any Testable type. ...看起来第一个脸红,就像它是一个可以是任何Testable类型的值的函数。 But in fact, it's a function that can be specialized to any Testable type -- and then only takes values of that type! 但实际上,它是一个可以专用于任何Testable类型的函数 - 然后接受该类型的值! This difference explains yet another reason the original test function didn't work. 这种差异解释了原始test功能不起作用的另一个原因。 You can't have multiple patterns with variables at different types, because the function only ever works on a single type at a time. 您不能在不同类型的变量中使用多个模式,因为该函数一次只能在一个类型上运行。

The ideas behind the classes NamedType and Testable above can be generalized a bit; 上面的NamedTypeTestable类背后的想法可以概括一点; if you do, you get the Typeable class suggested by hammar above. 如果你这样做,你会得到上面的Typeable建议的Typeable类。

I think now I've rambled more than enough, and likely confused more things than I've clarified, but leave me a comment saying which parts were unclear, and I'll do my best. 我觉得现在我已经絮絮叨叨了,可能比我澄清的事情更困惑,但是给我留言说哪些部分不清楚,我会尽我所能。

Is there a function that returns the type of an algebraic parameter (a 'show' for types)? 是否有一个返回代数参数类型的函数(类型的'show')?

I think Data.Typeable may be what you're looking for. 我认为Data.Typeable可能就是你要找的东西。

Prelude> :m + Data.Typeable
Prelude Data.Typeable> typeOf (1 :: Int)
Int
Prelude Data.Typeable> typeOf (1 :: Integer)
Integer

Note that this will not work on any type, just those which have a Typeable instance. 请注意,这不适用于任何类型,只有那些具有Typeable实例的类型。 Using the extension DeriveDataTypeable , you can have the compiler automatically derive these for your own types: 使用扩展DeriveDataTypeable ,您可以让编译器自动为您自己的类型派生这些:

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Typeable

data Foo = Bar
  deriving Typeable
*Main> typeOf Bar
Main.Foo

I didn't quite get what you're trying to do in the second half of your question, but hopefully this should be of some help. 在你的问题的后半部分,我没有完全得到你想要做的事情,但希望这应该有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM