第二个明显失败,因为"I am not a number!"
在该往返中抛出错误。有很多原因为什么在纯代码中抛出错误是危险的,而且类型类危险会继续并污染任何曾经导入代码的人的代码。
您可能会指出,这只是因为并非所有字符串都是有效数字。好像fromSI . toSI
总是麻烦,但toSI . fromSI
应该工作。也许它只影响像例化instance Num String
这样的东西,而如果我们改为使用我们的(toSI, froSI)
对来推导instance
的Integer
那String
我们会处于一个好位置。 也许吧。
让我们试试吧。 String
是Monoid
一个实例,看起来像这样
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
这似乎应该工作得很好。它真的是Integer
Integer
继承其“单向同构”与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
包定义之类的东西Sum
和Product
这表明该Integer
一直专注于只使用其Addition
性能。
就是这样,在一天结束时,您完全可以解决类型实例超出同构的问题。通常情况下,类型具有很多可以以这种方式滥用的同构和缩进,并且很难有真正的规范,守法的实例。当你找到一个代码时,通常值得把代码税明确写出来,即使你最终使用同构来完成。
当没有你有一个像newtype
文书规范的选择和库的快速访问哪些只是“下面”你newtype
层全部从普通GeneralizedNewtypeDeriving
扩展出路的Control.Newtype
package或直接启发Iso
, au
and auf
摆和整个Wrapped
, ala
, alaf
机制lens
。
本质上,这个机制是为了让它更容易地讨论各种同构,特别是那些由newtype
诱发的实例。
'get'? 'set'?我想你在问什么与Haskell所谓的推测非常相关。你的意思是自动类型转换,对吧?这不是一个好主意;它使您不能从双向类型推演的好处中获益,因为这样可以避免比一些显式转换函数可以合理引入的更多的样板。 - 另外,这些是特别糟糕的例子,因为这些类型甚至不是近似的同构。你会怎样处理像'get“bla”:: Int','set(-1):: Bool'或set [1,2,3] :: Maybe Int'这样的问题? – leftaroundabout
我认为应该可以在编译时用一个简单的实例声明替换声明,所以类型系统应该保持不变,尽管我同意这个提议与Haskell的派生机制非常不相关;也许名称变化是为了。 – user1502040
什么声明应该被什么取代?你能举一个你想如何使用它的例子吗? – leftaroundabout