我需要在* -> *
类型之间建立排序,因为一种类型的每个成员都可以由另一种类型表示。这是一种同态。
问题是我可以定义!<=!
关系的传递性,但类型检查器无法弄明白。它也非常含糊不清,Identity !<=! Maybe
可以从Identity !<=! Maybe
或Identity !<=! Identity !<=! Maybe
派生,...每个派生都有repr
的不同(但等效)定义。
所以我正在寻找其他方法来建立一种反思性和传递性的关系。
{-# LANGUAGE ScopedTypeVariables, TypeOperators, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, AllowAmbiguousTypes, OverlappingInstances #-}
import Control.Monad.Identity
import Data.Maybe
class x !<=! y where
repr :: x a -> y a
instance x !<=! x where
repr = id
instance Identity !<=! Maybe where
repr = return . runIdentity
instance Maybe !<=! [] where
repr = maybeToList
instance (x !<=! y, y !<=! z) => x !<=! z where
repr = r2 . r1
where r1 :: x a -> y a
r1 = repr
r2 :: y a -> z a
r2 = repr
注意:我在GHC 7.8上试过这个。您可能需要删除AllowAmbiguousTypes
。
编辑:我想做repr (Identity 3 :: Identity Int) :: [Int]
答案 0 :(得分:5)
问题在于我们无法让GHC对实例执行常规图搜索。在这种特殊情况下,如果GHC可以执行最短路径算法,那将是更好的,因为我们的函数随着路径中的每个中间表示而变慢。
但是,我们可以通过将传出边数限制为1来使每个图节点的搜索明确,GHC可以处理。这意味着每种类型最多只有一个直接表示:
{-# LANGUAGE FlexibleInstances, TypeOperators, MultiParamTypeClasses, FunctionalDependencies, UndecidableInstances, OverlappingInstances #-}
import Control.Monad.Identity
import Data.Maybe
class DirectRepr x y | x -> y where
directRepr :: x a -> y a
我们可以使用DirectRepr
:
instance DirectRepr Identity Maybe where
directRepr (Identity a) = Just a
instance DirectRepr Maybe [] where
directRepr = maybeToList
然后使用包装类<=
:
class x <= y where
repr :: x a -> y a
instance x <= x where
repr = id
instance (DirectRepr x y, y <= z) => x <= z where
repr = repr . directRepr
main = print (repr (Identity ()) :: [()]) -- [()]
它也适用于循环图,因为当我们达到<=
的反身性情况时,搜索会停止(感谢OverlappingInstances
):
data A a
data B a
data C a
instance DirectRepr A B where directRepr = undefined
instance DirectRepr B C where directRepr = undefined
instance DirectRepr C A where directRepr = undefined
foo :: A Int
foo = repr (undefined :: B Int)
如果起始类型导致循环,并且我们在循环中没有端点类型,则搜索卡住并且我们得到上下文溢出。这不应该让我们烦恼太多,因为这会使上下文溢出错误等同于普通的&#34;没有实例&#34;错误。
bar :: Maybe Int -- context overflow
bar = repr (undefined :: A Int)
答案 1 :(得分:2)
这可能无法通过推理来做到这一点。我使用Template Haskell创建了另一个解决方案,生成可以从更简单的实例派生的所有实例。库的用法如下所示:
$(makeMonadRepr ''Identity ''Maybe [e| return . runIdentity |])
$(makeMonadRepr ''Identity ''IO [e| return . runIdentity |])
$(makeMonadRepr ''Maybe [t| MaybeT IO |] [e| MaybeT . return |])
$(makeMonadRepr ''IO [t| MaybeT IO |] [e| MaybeT . liftM Just |])
$(makeMonadRepr ''Maybe TH.ListT [e| maybeToList |])
$(makeMonadRepr TH.ListT [t| Trans.ListT IO |] [e| Trans.ListT . return |])
$(makeMonadRepr ''IO [t| Trans.ListT IO |] [e| Trans.ListT . liftM (:[]) |])
$(makeMonadRepr [t| MaybeT IO |] [t| Trans.ListT IO |] [e| Trans.ListT . liftM maybeToList . runMaybeT |])
这将生成可以从反身性或传递性派生的所有实例。插入一个调用makeMonadRepr
的新节点后,会创建所有可派生边,因此用户可以扩展这样的结构。
这可能不是最优雅的解决方案,所以我愿意接受其他想法。