[英]Haskell get type of algebraic parameter
我有一个类型
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]
我正试图为PolyRing类型做一个很好的“show”。 特别是,我希望“show”打印出类型 'a'。 是否有一个返回代数参数类型的函数(类型的'show')?
我尝试这样做的另一种方法是使用模式匹配,但我遇到了内置类型和代数类型的问题。
我希望每个Integer,Int和Zq q都有不同的结果。 (玩具示例:)
test :: (Num a, IntegerAsType q) => a -> a
(Int x) = x+1
(Integer x) = x+2
(Zq x) = x+3
这里至少有两个不同的问题。
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
我有点看到它为什么抱怨,但我不知道如何解决这个问题。
谢谢
编辑:我正在尝试使用测试功能的一个更好的例子:
test :: (Num a) => a -> a
test (Integer x) = x+2
test (Int x) = x+1
test (Zq x) = x
即使我们忽略了这样一个事实:我无法以这种方式构造整数和Ints(仍然想知道如何!)这个'test'不能编译,因为:
Could not deduce (a ~ Zq t0) from the context (Num a)
我对此函数的下一次尝试是使用类型签名:
test :: (Num a, IntegerAsType q) => a -> a
这导致了新的错误
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 '=>'
我希望这会让我的问题更加清晰......
我不确定你使用该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) ++ ")"
如果我没有通过警告跟进这个答案,我不会做我的Stack Overflow任务:你要求的是非常非常奇怪的。 你可能正在以一种非常单一的方式做一些事情,并将全力以赴地对抗语言。 我强烈建议您的下一个问题是一个更广泛的设计问题,以便我们可以帮助您指导更加惯用的解决方案。
编辑
您的问题还有另外一半,即如何在输入上编写“模式匹配”的test
函数,以检查它是Int
, Integer
, Zq
类型等。您提供了这个暗示性代码片段:
test :: (Num a) => a -> a
test (Integer x) = x+2
test (Int x) = x+1
test (Zq x) = x
这里有几件事需要澄清。
Haskell有三个级别的对象:值级别,类型级别和种类级别。 价值层面的一些事例包括"Hello, world!"
, 42
,函数\\a -> a
,或者fix (\\xs -> 0:1:zipWith (+) xs (tail xs))
。 类型级别的一些示例包括Bool
, Int
, Maybe
, Maybe Int
和Monad m => m ()
。 类型级别的一些示例包括*
和(* -> *) -> *
。
水平是有序的; 值级别对象按类型级别对象分类,类型级别对象按类型级别对象分类。 我们使用::
编写分类关系,例如, 32 :: Int
或"Hello, world!" :: [Char]
"Hello, world!" :: [Char]
。 (对于此讨论,类型级别并不太有趣,但*
对类型进行分类,箭头类型对类型构造函数进行分类。例如, Int :: *
和[Int] :: *
,但是[] :: * -> *
。 )
现在,Haskell最基本的属性之一是每个级别都是完全隔离的。 你永远不会看到像"Hello, world!"
这样的字符串"Hello, world!"
在一个类型; 类似地,值级对象不会传递或操作类型。 此外,值和类型有单独的命名空间 。 以Maybe
为例:
data Maybe a = Nothing | Just a
该声明在类型级别创建一个新名称Maybe :: * -> *
,并且在值级别创建两个新名称Nothing :: Maybe a
和Just :: a -> Maybe a
。 一种常见的模式是对类型构造函数和它的值构造函数使用相同的名称,如果只有一个; 例如,您可能会看到
newtype Wrapped a = Wrapped a
它在类型级别声明一个新名称Wrapped :: * -> *
,同时声明一个不同的名称Wrapped :: a -> Wrapped a
在值级别。 一些特别常见(和令人困惑的例子)include ()
,它既是值级对象(类型()
),也是类型级对象(类型*
)和[]
,它们都是值级对象(类型[a]
)和类型级对象(类型* -> *
)。 请注意,值源级别和类型级别对象恰好在源代码中拼写相同这一事实只是巧合! 如果你想让读者感到困惑,你可以写得很好
newtype Huey a = Louie a
newtype Louie a = Dewey a
newtype Dewey a = Huey a
这三个声明中没有一个完全相互关联!
现在,我们可以最终解决上面的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
......但是,唉,它并不像那样。 不允许价值级别的东西依赖于类型级别的东西。 你可以做的是在Int
, Integer
和Zq 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
然后,当我们想要进行测试时,我们可以调用这些函数中的相应函数。 由于我们使用的是静态类型语言,因此这些函数中的任何一个都适用于任何特定变量。
现在,记住调用正确的函数有点繁琐,因此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
现在, 看起来有一个名为test
函数可以处理Int
, Integer
或Zq
的任何一个 - 但实际上有三个函数,编译器会透明地为你选择一个函数。 这是一个重要的见解。 test
类型:
test :: Testable a => a -> a
...看起来第一个脸红,就像它是一个可以是任何Testable
类型的值的函数。 但实际上,它是一个可以专用于任何Testable
类型的函数 - 然后只接受该类型的值! 这种差异解释了原始test
功能不起作用的另一个原因。 您不能在不同类型的变量中使用多个模式,因为该函数一次只能在一个类型上运行。
上面的NamedType
和Testable
类背后的想法可以概括一点; 如果你这样做,你会得到上面的Typeable
建议的Typeable
类。
我觉得现在我已经絮絮叨叨了,可能比我澄清的事情更困惑,但是给我留言说哪些部分不清楚,我会尽我所能。
是否有一个返回代数参数类型的函数(类型的'show')?
我认为Data.Typeable
可能就是你要找的东西。
Prelude> :m + Data.Typeable
Prelude Data.Typeable> typeOf (1 :: Int)
Int
Prelude Data.Typeable> typeOf (1 :: Integer)
Integer
请注意,这不适用于任何类型,只有那些具有Typeable
实例的类型。 使用扩展DeriveDataTypeable
,您可以让编译器自动为您自己的类型派生这些:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Typeable
data Foo = Bar
deriving Typeable
*Main> typeOf Bar
Main.Foo
在你的问题的后半部分,我没有完全得到你想要做的事情,但希望这应该有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.