[英]How to establish an ordering between types in Haskell
我需要在* -> *
类型之间建立排序,基于一种类型的每个成员可以由另一种类型表示。 这是同态。
问题是我可以定义!<=!
的传递性!<=!
关系,但类型检查无法搞清楚。 这也很模糊, Identity !<=! Maybe
Identity !<=! Maybe
可以从Identity !<=! Maybe
派生出来Identity !<=! Maybe
Identity !<=! Maybe
还是Identity !<=! Identity !<=! Maybe
Identity !<=! 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]
问题是我们无法让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)
如果起始类型导致循环,并且我们在循环中没有端点类型,则搜索卡住并且我们得到上下文溢出。 这不应该打扰我们,因为这会使上下文溢出错误等同于简单的“无实例”错误。
bar :: Maybe Int -- context overflow
bar = repr (undefined :: A Int)
仅通过推理可能无法做到这一点。 我使用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
的新节点后,将创建所有可makeMonadRepr
边,因此用户可以扩展这样的结构。
这可能不是最优雅的解决方案,所以我对其他想法持开放态度。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.