gpt4 book ai didi

haskell - 如何使用记录设计扩展

转载 作者:行者123 更新时间:2023-12-01 12:24:03 29 4
gpt4 key购买 nike

我想知道 Haskell 社区的人们如何处理以下设计。假设一个类似工作流的系统,您通过系统中的多个步骤传输一些数据(结构)。随着数据流经系统,越来越多的数据项将被添加到该结构中,而这些数据项在之前的步骤中是不可用的。现在我想确保无法访问先前步骤中不可用的数据项 - 最好通过编译时检查。

到目前为止,我提出了两种不同的方法。

方法 1:一遍又一遍地重新创建所有类型:

module Step1 where

data A = A { item1 :: SomeType }

module Step2 where

data B = B { item1 :: SomeType, item2 :: SomeOtherType }

fromAtoB :: A -> B

module Step3 where

data C = C { item1 :: SomeType, item2 :: SomeOtherType, item3 :: SomeOtherTypeAgain }

fromBtoC :: B -> C

显然,步骤越多,数据类型定义得越深越广,这就会变得非常繁重。

方法 2:组合类型:

module Step1 where

data A = A { item1 :: SomeType }

module Step2 where

data B = B { a :: A , item2 :: SomeOtherType }

fromAtoB :: A -> B

module Step3 where

data C = C { b :: B, item3 :: SomeOtherTypeAgain }

fromBtoC :: B -> C

这种方法有一个问题,即任何给定步骤的用户突然暴露于之前的所有步骤,因为对某些属性的访问与其他属性不同(例如,cInstance.b.a.Item1cInstance.Item1),尽管对于任何给定步骤的用户来说,数据结构自然是平坦的。事实上,他/她甚至不一定知道在他们自己的步骤之前还有步骤。在 OO 系统中,我会简单地从 B 扩展 C,从 A 扩展 B。

非常欢迎任何想法。

最佳答案

如果您想避免语言扩展,您提出的两个解决方案是可行的方法。对于嵌套的变体,我建议您 {-# UNPACK #-} 嵌套数据。这样您至少可以避免在运行时进行间接寻址。

如果您真的想要使用类似子类型的东西,请查看 this solution几天前我想到了。

但是,我认为对于这个问题,您最好采用一种通常用于阶段到阶段转换的数据的方法(GHC 使用类似的方法来处理 Haskell AST)。基本上,您创建一个 type familiy,通过给它们输入 () 直到正确的阶段,“隐藏”字段直到正确的阶段。

{-# LANGUAGE TypeFamilies, DataKinds #-}

data Stage = A | B | C

-- | A data type containing the final set of fields
data Complete (stage :: Stage) = Complete
{ item1 :: RestrictedUntilAfter A stage SomeType
, item2 :: RestrictedUntilAfter B stage SomeOtherType
, item3 :: RestrictedUntilAfter C stage SomeOtherTypeAgain
}

-- | Compares the two given stages to determine if the result type should be hidden
-- as `()` or not
type family RestrictedUntilAfter (s1 :: Stage) (s2 :: Stage) x :: * where
RestrictedUntilAfter B A _ = ()
RestrictedUntilAfter C A _ = ()
RestrictedUntilAfter C B _ = ()
RestrictedUntilAfter _ _ t = t

然后,通过管道的类型是Complete AComplete BComplete C。在某个阶段之前受限制的字段将在该阶段之前具有类型 ()

c1 = Complete { item1 = x, item2 = (), item3 = () } :: Complete A -- x :: SomeType
c2 = Complete { item1 = x, item2 = y, item3 = () } :: Complete B -- y :: SomeOtherType
c3 = Complete { item1 = x, item2 = y, item3 = z } :: Complete C -- z :: SomeOtherTypeAgain

(类型族最好是开放的,或者以不同的顺序进行模式匹配,但思想是一样的)

编辑

正如我所怀疑的,有一种更清洁的家庭方法。事实上,使用这种方法,您甚至不需要定义任何类型系列,并且在添加阶段和字段时它可以根据 LOC 很好地扩展。最后,它更加灵活。但是,它确实取决于 type-list .

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

import Data.Type.List
import Data.Type.Bool

data Stage = A | B | C

type RestrictedTo stage validStages ty = If (Find stage validStages) ty ()

-- | A data type containing the final set of fields
data Complete (stage :: Stage) = Complete
{ item1 :: stage `RestrictedTo` [A,B,C] SomeType
, item2 :: stage `RestrictedTo` [B,C] SomeOtherType
, item3 :: stage `RestrictedTo` [C] SomeOtherTypeAgain
}

现在,您甚至可以在 AC 阶段(但不是 B): item4::阶段 `RestrictedTo` [A,C] SomeOtherOtherType

关于haskell - 如何使用记录设计扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41633607/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com