简体   繁体   English

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

[英]How to establish an ordering between types in Haskell

I need to establish ordering between * -> * types based on that each member of one type can be represented by another. 我需要在* -> *类型之间建立排序,基于一种类型的每个成员可以由另一种类型表示。 This is a homomorphism. 这是同态。

The problem is that I can define the transitivity of the !<=! 问题是我可以定义!<=!的传递性!<=! relation, but the type checker cannot figure it out. 关系,但类型检查无法搞清楚。 It is also very ambiguous, Identity !<=! Maybe 这也很模糊, Identity !<=! Maybe Identity !<=! Maybe could be derived from Identity !<=! Maybe Identity !<=! Maybe可以从Identity !<=! Maybe派生出来Identity !<=! Maybe Identity !<=! Maybe or Identity !<=! Identity !<=! Maybe Identity !<=! Maybe还是Identity !<=! Identity !<=! Maybe Identity !<=! Identity !<=! Maybe Identity !<=! Identity !<=! Maybe , ... Each derivation comes with a different (but equivalent) definition for repr . Identity !<=! Identity !<=! Maybe ,......每个派生都有一个不同的(但等价的) repr定义。

So I'm looking for other ways to create a reflexive and transitive relationship. 所以我正在寻找其他方法来创造一种反思性和传递性的关系。

{-# 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

note: I tried this on GHC 7.8. 注意:我在GHC 7.8上试过这个。 You may have to remove AllowAmbiguousTypes . 您可能必须删除AllowAmbiguousTypes

Edit: I would like to do something like repr (Identity 3 :: Identity Int) :: [Int] 编辑:我想做一些像repr (Identity 3 :: Identity Int) :: [Int]

The problem is that we can't get GHC to perform a general graph search for instances. 问题是我们无法让GHC对实例执行常规图搜索。 In this particular case it would be even nice if GHC could perform a shortest path algorithm, since our function gets slower with each intermediate representation in the path. 在这种特殊情况下,如果GHC可以执行最短路径算法,那将是更好的,因为我们的函数随着路径中的每个中间表示而变慢。

However, we can make the search unambiguous at each graph node, by restricting the number of outgoing edges to one, and GHC can handle that. 但是,我们可以通过将传出边的数量限制为1来使搜索在每个图节点处明确,并且GHC可以处理该搜索。 This means that each type has at most one direct representation: 这意味着每种类型最多只有一个直接表示:

{-# 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

We can build a graph with DirectRepr : 我们可以用DirectRepr构建一个图形:

instance DirectRepr Identity Maybe where
    directRepr (Identity a) = Just a

instance DirectRepr Maybe [] where
    directRepr = maybeToList

and then walk it with a wrapper class <= : 然后使用包装类<=遍历它:

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 ()) :: [()]) -- [()]

It works with cyclic graphs, too, since the search stops when we hit the reflexivity case for <= (thanks to OverlappingInstances ): 它也适用于循环图,因为当我们达到<=的反身性情况时搜索停止了(感谢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)

If the starting type leads to a cycle, and we don't have the endpoint type in the cycle, the search gets stuck and we get a context overflow. 如果起始类型导致循环,并且我们在循环中没有端点类型,则搜索卡住并且我们得到上下文溢出。 This shouldn't bother us overmuch, since this makes the context overflow error equivalent to a plain "no instance" error. 这不应该打扰我们,因为这会使上下文溢出错误等同于简单的“无实例”错误。

bar :: Maybe Int -- context overflow
bar = repr (undefined :: A Int)

This may not be possible to do this only by inference. 仅通过推理可能无法做到这一点。 I made another solution using Template Haskell, generating all instances that can be derived from simpler ones. 我使用Template Haskell创建了另一个解决方案,生成可以从更简单的实例派生的所有实例。 The usage of the library looks like the following: 库的用法如下所示:

$(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 |])

This generates all instances that can be derived from reflexivity or transitivity. 这将生成可以从反身性或传递性中导出的所有实例。 After inserting a new node with calling makeMonadRepr all the derivable edges are created, so a structure like this can be extended by the user. 插入一个调用makeMonadRepr的新节点后,将创建所有可makeMonadRepr边,因此用户可以扩展这样的结构。

This may not be the most elegant solution, so I'm open for other ideas. 这可能不是最优雅的解决方案,所以我对其他想法持开放态度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM