我正在尝试编写一个带有常规函数的arrow转换器,并将它们转换为抽象值的计算。如果我们有一个“来源”箭头,
f :: Int -> Int
f x = x + 1
然后目标是让 f 在提升的[sic?]抽象值类型上工作,在这个例子中
f' :: AV Int -> AV Int
f' (Const x) = Const (f x)
-- pass along errors, since AV computation isn't always defined
-- or computable in the case of errors
f' (Error s) = Error s
-- avRep = "abstract representation". Think of symbolic math manipulation or ASTs.
f' (Abstract avRep) = AVRepPlus avRep (AVRepConst 1)
但是,为了成功实现此箭头,需要提升几种类型,以便在任意深度上具有具有一些具体和一些抽象值的异构数据结构。我最终做的是为常规的haskell构造函数添加特殊类型,例如如果
g = uncurry (+) -- i.e. g (x, y) = x + y
然后我为(,),元组构造函数
添加一个抽象表示AVTuple :: AV a -> AV b -> AV (a, b)
并且 g 的代码被解除为[展开一点],
g' (AVTuple (AVConst a) (AVConst b)) = (AVConst (g (a, b)))
g' (AVTuple (AVError e) _) = (AVError e)
-- symmetric case here, i.e. AVTuple _ (AVError e)
g' (AVTuple a@(AVTuple _ _) b) = -- recursive code here
AVEither也需要做同样的事情。这最终将成为很多案例。这有什么好办法吗?
我是Haskell的新手,所以请给我参考或半详细解释;可能最接近我读过的是SYBR纸(废弃样板转速)第1-3节。
非常感谢你!
答案 0 :(得分:1)
让我看看我是否理解你在这之后所做的事情。您有一个类型AV a
,它描述了一个产生a
类型的计算,其中该计算的结构可以以允许检查的方式保存。您想要一种方法将任意函数提升到AV
上的操作,保留结构,而不必为每个操作创建特殊情况。
通常,要将功能提升到某个结构,可以使用Functor
和Applicative
。但是,这样做的直接方法包括转换结构并直接应用提升函数,而不是将函数应用程序保留为结构的一部分。
你想要什么更尴尬,这就是原因:
假设我们有一些我们想要解除的函数,以及两个适当类型的抽象值来应用它:
x :: AV A
x = ...
y :: AV B
y = ...
f :: A -> B -> C
f = ...
假设有一个函数liftAV2
可以满足您的需求。我们希望lift2 f
的类型为AV A -> AV B -> AV C
,就像liftA
的{{1}}一样。
稍后,我们希望通过恢复Applicative
,lift2 f
和f
的值来检查使用x
生成的计算。让我们说现在我们只想提取第一个参数。假设存在执行此操作的函数y
,以便extractArg1
= extractArg1 (liftAV2 f x y)
。 x
的类型是什么?在 context 中,我们知道它应该具有类型extractArg1
。但它总体上会有什么类型?像AV C -> AV A
这样的东西?这是错误的,因为结果不只是任何类型AV c -> AV a
,它是用于构造a
值的任何类型。假设我们运行的值是使用AV c
的结果构造的,我们知道问题类型存在,但我们无法一般地找到它。
这是我们进入适当的土地,存在类型。老实说使用它们,不仅仅是通常情况下误用类型类。
你可以通过一些努力完成你所追求的目标,但这已经进入了相当先进的领域。您可能希望将 GADTs 用于初学者,不过我认为您可能已经这样做了。对于存在类型,它也往往非常笨拙,因为你只能在有限的语境中知道它们是什么。
在您的特定情况下,给liftAV2 f
两个类型参数可能更容易:一个表示计算的最终类型,一个表示计算结构,例如:
AV
然后,为了检查计算,您可以查看第一种类型以了解您拥有的内容;为了构建计算,您可以查看第二个以确保类型匹配。评估函数将具有类似data f :$ x = ...
data AV structure result where
...
AVApply :: AV f (a -> b) -> AV x a -> AV (f :$ x) b
的类型,从而丢弃结构。你也可以使用结构类型“解包”计算,抛弃结果类型,如果你需要拆开结构,比如说,打印它。
答案 1 :(得分:0)
我喜欢考虑这个问题的方式,当我想谈论一些“额外的数据”时,我会使用Functor
实例(取决于“额外的一点”是什么,我可能在实际上是在谈论Applicative
或Monad
)。
另一方面,我使用Arrow
实例来讨论“功能少一点”:箭头让你可以用与函数相同的方式定义可以组合在一起的东西,但是你可以添加额外的禁止某些结构的结构或限制(例如没有ArrowChoice
或ArrowLoop
的箭头)。
您希望完成的内容并不完全清楚,但听起来您实际上是在AV
类型构造函数中包装数据。在这种情况下,您可能希望AV
为Functor
的实例,并为Functor
添加(AV a, AV b) => AV (a, b)
个实例,同样为AV
包裹Either
{{1}} 1}}。