是否有可能以强类型的方式表示这种转变?

时间:2015-06-25 14:40:02

标签: haskell types f#

我正在寻找像(在F#中)的转换:

type Test = TBool of bool | TString of string

type TestList = TLBool of bool list | TLString of string list

let transform : Map<int, Test> list -> Map<int, TestList> = ??

有没有办法对此进行编码,以便我们“知道”当Map包含异构值时,每个位置的值在包含列表的元素中是相同的类型?一旦构造,地图将是静态大小,并且每个列表元素都是相同的,但是事先不知道大小,所以我基本上想要生成未知大小的元组/记录。

修改

我认为我的例子不清楚。我所追求的根源是能够采用两个变量大小的集合,它们在给定位置的值总是相同的类型,但集合本身可以包含多种类型的值,并使用它们将它们“压缩”在一起。知道在给定位置两个值是相同的类型。具体来说,我不想重新检查它们是否相同并传播它们变化的条件(作为某种错误),因为我在最初创建集合时已经完全这样做了。

修改2

从下面发布的评论:我本质上想要异构列表(我使用地图,因为我的索引可以是稀疏的,但我总是可以使用带有索引映射的列表),但是附加约束是异构列表的两个实例可以“压缩在一起”,并且给定索引处的值具有相同的类型。

4 个答案:

答案 0 :(得分:1)

  

编辑 [...]我所追求的根源是能够获取两个可变大小的集合,它们在给定位置的值始终是相同的类型,但是该集合本身可以包含多种类型的值,并使用在给定位置两个值是相同类型的知识将它们“压缩”在一起。

在最广泛的阅读中,这从根本上要求集合的类型编码哪个位置包含哪个元素类型,并且这会侵入依赖类型区域。

如果有一个固定形状的集合可以在编译时确定,那么很容易 - 你只需要写一个类型,其值都具有该形状。

答案 1 :(得分:0)

您可以定义一个具有所需属性的transformMap函数(即保留键并将字符串转换为字符串列表并将bool转换为bool列表)。该函数仍然可以完全参数化它与值的关系:

let transformMap fstr fbool map = 
  map |> Map.map (fun k v -> 
    match v with 
    | TBool b -> TLBool(fbool k b)
    | TString s -> TLString(fstr k s) )

您使用transformMap在地图上执行的任何转化都具有您想要的属性。如果你想要更严格,你可以在Map<'K, TestList>上写一个包装器并隐藏内部表示,这将给你一个强有力的保证。在实践中,我认为公开内部结构可能是合理的,但是手动检查您只使用正确的转换函数操作地图。

使用此函数将每个值转换为单个列表的示例如下所示:

Map.empty |> transformMap
  (fun k s -> [s])
  (fun k b -> [b])

答案 2 :(得分:0)

如果您可以使用Test list代替TestList,则转换相当简单:

let transform listOfMaps : Map<int, Test list> =
    listOfMaps
    |> Seq.collect Map.toSeq 
    |> Seq.groupBy fst
    |> Seq.map (fun (i, s) -> 
        let xs = s |> Seq.map snd |> Seq.toList
        i, xs)
    |> Map.ofSeq

至于在类型中捕捉你的假设,我怀疑它不可能或者比它的价值更多的努力。

答案 3 :(得分:0)

包含在某些f中的元素的异构列表是:

{-# LANGUAGE GADTs, DataKinds, TypeOperators #-}

infixr 5 :::

data HList f as where
    HNil  :: HList f '[]
    (:::) :: f a -> HList f as -> HList f (a ': as)

例如,

xs1 :: HList [] [Bool, Int]
xs1 = [True, False] ::: [1..3] ::: HNil

xs2 :: HList [] [Bool, Int]
xs2 = [True]        ::: [1..5] ::: HNil

可以轻松压缩两个HList []

hzip :: HList [] as -> HList [] as -> HList [] as
hzip  HNil         HNil        = HNil
hzip (xs ::: xss) (ys ::: yss) = (xs ++ ys) ::: hzip xss yss

或更普遍(并使用Rank2Types扩展名):

hzipWith :: (forall a. f a -> g a -> h a) -> HList f as -> HList g as -> HList h as
hzipWith f  HNil       HNil      = HNil
hzipWith f (x ::: xs) (y ::: ys) = f x y ::: hzipWith f xs ys

hzip :: HList [] as -> HList [] as -> HList [] as
hzip = hzipWith (++)

然后(需要FlexibleInstancesFlexibleContexts

instance Show (HList f '[]) where
    show HNil = "HNil"

instance (Show (f a), Show (HList f as)) => Show (HList f (a ': as)) where
    show (x ::: xs) = show x ++ " ::: " ++ show xs 

main = print $ hzip xs1 xs2

打印[True,False,True] ::: [1,2,3,1,2,3,4,5] ::: HNil