jcr*_*vao 10 haskell typeclass
是否可以为"非monad"定义实例约束,以便定义两个非重叠实例,一个用于monadic值,另一个用于非monadic值?
一个简化的例子:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverlappingInstances #-}
class WhatIs a b | a -> b where
whatIs :: a -> b
instance (Show a) => WhatIs a String where
whatIs = show
instance (Monad m, Functor m, Show a) => WhatIs (m a) (m String) where
whatIs x = fmap show x
main :: IO ()
main = do
let x = 1 :: Int
putStrLn "----------------"
{-print $ whatIs (1::Int)-}
print $ whatIs (Just x)
putStrLn "--- End --------"
Run Code Online (Sandbox Code Playgroud)
因此,我使用FunctionalDependencies来避免类型注释,但当然,编译器抱怨
Functional dependencies conflict between instance declarations:
instance [overlap ok] Show a => WhatIs a String
-- Defined at test.hs:10:10
instance [overlap ok] (Monad m, Functor m, Show a) =>
WhatIs (m a) (m String)
-- Defined at test.hs:13:10
Run Code Online (Sandbox Code Playgroud)
因为a
可以承担价值m a
,从而产生冲突.
但是,如果我可以用以下内容替换第一个实例:
instance (NotAMonad a, Show a) => WhatIs a String where
whatIs = show
Run Code Online (Sandbox Code Playgroud)
这个问题不会出现.
到目前为止,我发现这个非常旧的电子邮件似乎提出了一个有点相关的解决方案,但我不确定是否有新技术来解决这个问题...
我还发现了约束包,我确信它对这种情况有用,但是(简单)示例中却非常缺乏.
有线索吗?
编辑:在user2407038之后正确回答.
所以,我尝试下面的user2407038答案,确实我设法编译提供的示例.结论?我不应该这么简化这个例子.在考虑了我的实际例子后,我能够将其减少到这个:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module IfThenElseStackExchange where
class IfThenElse a b c d | a b c -> d where
ifThenElse :: a -> b -> c -> d
instance (Monad m) => IfThenElse (m Bool) (m b) (m b) (m b) where
ifThenElse m t e = do
b <- m
if b then t else e
instance (Monad m) => IfThenElse (m Bool) b b (m b) where
ifThenElse ma t e = do
a <- ma
return $ if a then t else e
Run Code Online (Sandbox Code Playgroud)
但我仍然遇到了可怕的Functional dependencies conflict between instance declarations
错误.为什么?=>
(实例头部,如用户2407038及时注明)之后的部分实际上是完全不同的,因此它甚至不符合OverlappingInstances,因为编译器可以选择最具体的部分.
那又怎样?
错误信息一如既往地暗示错误.a b c d | a b c -> d
上述代码不尊重该部分.所以我终于尝试了这个:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module IfThenElseStackExchange where
class IfThenElse a b c d | a b c -> d where
ifThenElse :: a -> b -> c -> d
instance (Monad m, c ~ b) => IfThenElse (m Bool) (m b) (m b) (m c) where
ifThenElse m t e = do
b <- m
if b then t else e
instance (Monad m, c ~ b) => IfThenElse (m Bool) b b (m c) where
ifThenElse ma t e = do
a <- ma
return $ if a then t else e
Run Code Online (Sandbox Code Playgroud)
Etvoilà!
通过(m b)
在最后一个参数中使用,我试图指出最终结果与第二个和第三个参数具有相同的类型.但问题似乎是FunctionalDependencies
扩展不会使类型的实例选择为OverlappingInstances,因此为其目的考虑b
和(m b)
"相同".这种解释是正确的,还是我还缺少某些东西?
我仍然可以"告诉"编译器c
与b
使用约束的类型相同c ~ b
,从而达到预期的结果.
在阅读了更多有关此内容的材料之后,我高度推荐阅读Oleg的这篇文章,他将他以前的解决方案概括为I和user2407038.我发现它很容易接近.
如果我对上面的FunctionalDependencies的解释是正确的,并且TypeFamilies被呈现为同一问题域的更灵活的解决方案,我想知道是否可以使用它们以另一种方式解决这个问题.当然,上面提到的Oleg解决方案肯定会使用它们.
use*_*038 10
你不需要一个NotAMonad
类,或者更确切地说,WhatIs
就是这个类.
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances,
TypeFamilies, UndecidableInstances, IncoherentInstances #-}
class WhatIs a b | a -> b where
whatIs :: a -> b
instance (Show a, b ~ String) => WhatIs a b where
whatIs = show
instance (Monad m, Functor m, Show a) => WhatIs (m a) (m String) where
whatIs x = fmap show x
Run Code Online (Sandbox Code Playgroud)
你并不严格需要,IncoherentInstances
但如果你想要whatIs (1 :: Num a => a)
工作,你需要它.
这可能不是做你想做的最好的方法,但你的用例并不清楚.
编辑:更多解释:首先:这些实例不重叠!我列出了语言编译指示的完整列表.你得到的错误说"实例声明之间的功能依赖性冲突".说你有以下内容:
class C a b | a -> b
Run Code Online (Sandbox Code Playgroud)
假设你有两个实例C
:C a b
,C c d
(这里a
不是一个严格的类型变量;它只是任何haskell类型).如果a
是实例化c
(反之亦然),则b
必须是实例化d
.这个一般规则可能有点抽象,所以让我们来看看你的代码:
instance (Show a) => WhatIs a String where
instance (Monad m, Functor m, Show a) => WhatIs (m a) (m String) where
Run Code Online (Sandbox Code Playgroud)
既然a
是任何类型,你就是声明'对于任何类型a,whatIs a == String'.第二个实例声明'对于任何类型(ma),whatIs(ma)==(m String)'.显然m a
是a
(任何类型是自由类型变量String
的实例化)的实例化,但绝不是实例化m String
.
为什么这有关系?当编译器检查fundeps是否冲突时,它只查看实例头 ; 也就是说,右边的部分=>
.因此,
instance (Show a, b ~ String) => WhatIs a b where
Run Code Online (Sandbox Code Playgroud)
正在说'对于任何类型a,b,what is a = = b'.显然,既然a
和b
都是自由变量,它们可以用任何其他类型实例化.所以如果a == (m a0)
你能自由地说出来的话b == (m String)
.当且仅当第一个实例是匹配的实例时,b
必须是字符串的事实才会被知道.
因为任何类型的匹配a
和b
,WhatIs (IO ()) b
相匹配的第一个实例.使用第二个实例是因为编译器将尝试按特定顺序匹配实例.您可以在此处找到用于确定特异性的"规则"..简单的解释是WhatIs a b
匹配更多的东西,因此它更通用,并将在以后使用.(事实上,实例C a0 a1 a2 .. an
中a*
是一个不同的类型变量,是最常见的实例,并且总是最后尝试)
编辑:这是您的问题的一般解决方案.有了这段代码,whatIs = f_map show
.
归档时间: |
|
查看次数: |
227 次 |
最近记录: |