Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 如何使用记录设计扩展_Haskell_Functional Programming_Records_Algebraic Data Types - Fatal编程技术网

Haskell 如何使用记录设计扩展

Haskell 如何使用记录设计扩展,haskell,functional-programming,records,algebraic-data-types,Haskell,Functional Programming,Records,Algebraic Data Types,我想知道Haskell社区的人是如何处理以下设计的。 假设是一个类似工作流的系统,其中您通过系统中的多个步骤传递一些数据(结构)。 随着数据在系统中流动,越来越多的数据项将添加到该结构中,而这些数据项在前面的步骤中不可用。 现在,我想确保在前面的步骤中不可用的数据项是不可访问的,最好是通过编译时检查 到目前为止,我提出了两种不同的方法 方法1:反复重新创建所有类型: module Step1 where data A = A { item1 :: SomeType } module

我想知道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.Item1
cInstance.Item1
),即使对于任何给定步骤的用户,数据结构自然是平坦的。 事实上,他/她甚至不一定知道在他们自己的步骤之前有步骤。在OO系统中,我将简单地从B扩展C,从A扩展B


任何想法都是非常受欢迎的。

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

如果你真的想使用一些子类型,比如,看看我几天前提出的

然而,我认为对于这个问题,最好采用一种通常用于在各个阶段之间转换数据的方法(GHC使用类似的方法来处理Haskell AST)。基本上,您可以创建一个
类型family
,通过在正确的阶段之前输入
()
,将字段“隐藏”到正确的阶段

{-# 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
然后,经过管道的类型是
完成A
完成B
完成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进行扩展。最后,它更加灵活。然而,这确实取决于


现在,您甚至可以在阶段
a
C
(但不是
B
)中有一个非
()
的字段:
item4::stage`RestrictedTo`[a,C]其他类型

“这种方法存在一个问题,即任何给定步骤的用户都会突然暴露在前面的所有步骤中。”对于任何给定步骤的用户,期望通过
someInstance.itemX
vs
someInstance.b.a.itemX
访问任何属性,以实现类型族技巧(第4页)@BenjaminHodgson谢谢!在这一点上,我假设一个场总是从受限变为可用(再也不会回来),所以我只需要一个类型族,所以我玩得更快更脏。为了表示不同阶段字段的任意屏蔽,每个field.thx需要一个类型族作为答案。很不错的。如果不使用类型族而只使用更高级的类型,是否可以模拟相同的效果?@robkuz我不这么认为,但如果您不想自己定义任何类型族,请参阅我的编辑。
{-# 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
  }