2011-09-03 79 views
3

的获得I型有型哈斯克尔代数参数

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型一个不错的“秀”。特别是,我想让“秀”打印出型号'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这种方式(仍想知道如何!),这种“测试”并没有编译,因为:

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

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

回答

4

我不知道你在与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) ++ ")" 

我不会做我的堆栈溢出的责任,如果我做不要跟着这个答案提出警告:你要求的是非常非常奇怪的。你可能正在以非常自由的方式做一些事情,并且将以整个方式与语言作斗争。我强烈建议您的下一个问题是一个更广泛的设计问题,以便我们可以帮助您指导一个更习惯的解决方案。

编辑

还有一个半到你的问题,即如何写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 -> afix (\xs -> 0:1:zipWith (+) xs (tail xs))。类型级别的东西的一些示例包括Bool,Int,Maybe,Maybe IntMonad m => m()。类似的东西的一些例子包括*(* -> *) -> *

水平是有序的;值级别对象按类型级别对象进行分类,并且类型级别对象按类别级别对象进行分类。我们使用::来编写分类关系,例如,32 :: Int"Hello, world!" :: [Char]。 (那种水平是不是这个讨论太有趣了,但*分类类型和箭头种分类型构造。例如,Int :: *[Int] :: *,但[] :: * -> *)。现在

,哈斯克尔的最基本属性之一每个级别都是完全隔离的。你永远不会在类型中看到像"Hello, world!"这样的字符串;同样,价值水平的对象不会传递或操作类型。而且,对于值和类型,还有单独的名称空间。就拿Maybe的例子:

data Maybe a = Nothing | Just a 

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

newtype Wrapped a = Wrapped a 

其在类型级别声明了一个新的名字Wrapped :: * -> *,同时在价值层面宣布一个明确的名称Wrapped :: a -> Wrapped a。一些特别常见的(和混乱的例子)包括(),这既是一个值级对象(的()型)和一种类型的级对象(的种类*),和[],它是(的类型两者的值级对象[a])和类型级别的对象(种类* -> *)。请注意,价值级别和类型级别的对象在源代码中拼写相同的事实只是一个巧合!如果你想混淆你的读者,你可以写得很好

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

这三个声明根本没有关系!

现在,我们终于可以解决上面test出错的问题了:IntegerInt不是值构造函数,所以它们不能在模式中使用。请记住 - 值级别和类型级别是孤立的,因此您不能将类型名称放入值定义中!现在,你可能希望你写test'代替:

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

...但很可惜,它不完全是这样工作的。价值层面的东西不允许依赖于类型层次的东西。你可以做的是写在每个IntInteger,并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 

然后我们就可以调用这些功能的合适的一个,当我们想要做一个测试。由于我们使用的是静态类型语言,因此这些函数中的其中一个函数将适用于任何特定的变量。现在

,这是一个有点麻烦记住调用正确的函数,所以哈斯克尔提供了一个轻微的便利:可以让编译器选择在编译的时候,这些功能为你一个。这种机制是类背后的重要理念。它看起来像这样:

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一个单一的功能,可以处理任何的IntInteger,或者数字Zq的 - 但实际上有三个功能,并且编译器是透明地为你选择一个。这是一个重要的见解。该类型的test

test :: Testable a => a -> a 

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

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

我想现在我已经天马行空绰绰有余,并且很可能混淆更多的东西比我清楚,但给我留下了评论说法,其中部分都不清楚,我会尽我所能。

+0

hammar回答了我的问题的第一部分。不过,我仍然对如何进行模式匹配感兴趣。你可以在Int和Integer模式匹配吗? – crockeea

+0

我欣赏这个警告,并意识到我可能正在挖一个洞... 让我试着给一个函数的更好的例子,我可能实际上需要我所要求的: test::(Num a)=> a - > a test(Int x)= x + 2 test (Zq x)的类型参数,所以我可以修改,如果我需要。更重要的是,它不会编译(即使我拿出不可构造的Int和Integer行)。随着类型签名给定的,我得到的错误:从上下文(民一) 无法推断(一〜ZQ T0) – crockeea

+0

似乎有在这里是一些误解关于类型。给我几分钟,我会更新我的答案,讨论我能做些什么。 –

2

Is there a function that returns the type of an algebraic parameter (a 'show' for types)?

我认为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 

我不太明白你想在下半年做什么你问题,但希望这应该有一些帮助。