2013-08-29 50 views
1

我很抱歉,如果这个问题似乎病了深思熟虑的,但我不知道是否有可能定义一个一致的语义类似下面哈斯克尔:建议导出机制哈斯克尔

derive Num String from Int where 
    get = read 
    set = show 

derive Ord Bool from Integer where 
    get = fromEnum 
    set = toEnum 

derive (Monad, Functor) Maybe from [] where 
    get (Just x) = [x] 
    get Nothing = [ ] 
    set [x]  = Just x 
    set [ ]  = Nothing 

我没有理由不这样做,在某些情况下它似乎会减少样板,但我不知道是否(如果是这样,多么容易)这可以实施。

编辑:

我的意图是为例如,第一个例子被替换为这样的东西:

instance Num String where 
    get = read :: String -> Int 
    set = show :: Int -> String 
    a + b = set $ get a + get b 
    a * b = set $ get a * get b 
    ... 
+3

'get'? 'set'?我想你在问什么与Haskell所谓的推测非常相关。你的意思是自动类型转换,对吧?这不是一个好主意;它使您不能从双向类型推演的好处中获益,因为这样可以避免比一些显式转换函数可以合理引入的更多的样板。 - 另外,这些是特别糟糕的例子,因为这些类型甚至不是近似的同构。你会怎样处理像'get“bla”:: Int','set(-1):: Bool'或set [1,2,3] :: Maybe Int'这样的问题? – leftaroundabout

+0

我认为应该可以在编译时用一个简单的实例声明替换声明,所以类型系统应该保持不变,尽管我同意这个提议与Haskell的派生机制非常不相关;也许名称变化是为了。 – user1502040

+0

什么声明应该被什么取代?你能举一个你想如何使用它的例子吗? – leftaroundabout

回答

4

你在这里描述的基本上是通过同构定义类实例。这明显可以通过定义getset函数来实现,但我们将其称为tofro,以便更清楚一点。

toSI :: String -> Integer 
fromSI :: Integer -> String 

instance Num String where 
    a + b = fromSI $ (toSI a) + (toSI b) 
    abs = fromSI . abs . toSI 
    ... 

这些确实是很容易的定义写,有可能是通过在(to, fro)自动升降类的实例,这个系统必须认真遵循许多规则,为了不污染全局类型类减少样板一些价值实例空间与垃圾。

特别是,我们可能会问(to, fro)形成一个同构。这意味着在双向往返是身份。换言之,这意味着给定任何整数n,我不应该能够通过任何方式区分(froSI (toSI n))n(好吧,有些东西,比如计算速度会被忽略)。此外,我必须拥有的任何字符串stoSI (froSI s)必须与n无法区分。

第二个明显失败,因为"I am not a number!"在该往返中抛出错误。有很多原因为什么在纯代码中抛出错误是危险的,而且类型类危险会继续并污染任何曾经导入代码的人的代码。

您可能会指出,这只是因为并非所有字符串都是有效数字。好像fromSI . toSI总是麻烦,但toSI . fromSI应该工作。也许它只影响像例化instance Num String这样的东西,而如果我们改为使用我们的(toSI, froSI)对来推导instanceIntegerString我们会处于一个好位置。 也许吧。

让我们试试吧。 StringMonoid一个实例,看起来像这样

instance Monoid [a] where  -- if a ~ Char then this is String 
    mempty = [] 
    mappend as bs = as ++ bs 

如果我们通过mappend = toSI . mappend . fromSI实施mappend它为我们提供了“串联整数”像

(1 <> 2) <> 3 == 123 
1 <> (2 <> 3) == 123 
(0 <> 1) <> 1 == 11 
0 <> (1 <> 1) == 11 

,如果我们仔细定义"" -> 0而不是使它失败,那么我们也可以得到一个有用的mempty

mempty = toSI mempty 

这似乎应该工作得很好。它真的是IntegerInteger继承其“单向同构”与String(想想为什么我必须在这里使用Integer,而不是Int)。更具体地说,我们无法通过从Monoid类型类中的函数构建的任何测试来区分toSI (fromSI n)n,所以它足够“足够”来进行此映射。

但是后来我们碰到了另一个问题。 Integer已经有一个Monoid实例。它已经拥有了其中的约10%,最流行的是乘法和加法

instance Monoid Integer   instance Monoid Integer 
    mempty = 0      mempty = 1 
    mappend = (+)      mappend = (*) 

所以,我们通过挑选这些情况中的任何一个是规范类型类的实例含半幺群失去了大量的信息。 Integer通过它的“单向同构”(又名“缩回”)与String变成Monoid更加准确,但也可以通过剥离来增加,但也可以通过剥离来增加乘法。

我们真的要保持信息的周围这就是为什么Monoid包定义之类的东西SumProduct这表明该Integer一直专注于只使用其Addition性能。

就是这样,在一天结束时,您完全可以解决类型实例超出同构的问题。通常情况下,类型具有很多可以以这种方式滥用的同构和缩进,并且很难有真正的规范,守法的实例。当你找到一个代码时,通常值得把代码税明确写出来,即使你最终使用同构来完成。

当没有你有一个像newtype文书规范的选择和库的快速访问哪些只是“下面”你newtype层全部从普通GeneralizedNewtypeDeriving扩展出路的Control.Newtype package或直接启发Iso, au and auf摆和整个Wrapped, ala, alaf机制lens

本质上,这个机制是为了让它更容易地讨论各种同构,特别是那些由newtype诱发的实例。