如何在Haskell中建立类型之间的排序

时间:2014-07-16 07:48:21

标签: haskell typeclass

我需要在* -> *类型之间建立排序,因为一种类型的每个成员都可以由另一种类型表示。这是一种同态。

问题是我可以定义!<=!关系的传递性,但类型检查器无法弄明白。它也非常含糊不清,Identity !<=! Maybe可以从Identity !<=! MaybeIdentity !<=! 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]

之类的事情

2 个答案:

答案 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的新节点后,会创建所有可派生边,因此用户可以扩展这样的结构。

这可能不是最优雅的解决方案,所以我愿意接受其他想法。