Haskell 类型对库不透明但特定于应用程序的函数
我想传递一个函数:Haskell 类型对库不透明但特定于应用程序的函数,haskell,types,Haskell,Types,我想传递一个函数: f :: a -> CmdRequest -> (a, CmdResponse) 从应用程序A到系统S 定义CmdRequest和CmdResponse 初始化时:给定/存储f 初始化时:存储a的初始值(作为不透明值,S无法看到任何a结构) 调用f(作为系统操作的一部分) 传递它以前存储的值(来自以前的调用或系统初始化) 存储返回类型的第一个组件 A 定义f 初始化时:将f传递到S 初始化时:将a的初始值传递给S 可以为a使用特定类型(例如Data.Ha
f :: a -> CmdRequest -> (a, CmdResponse)
从应用程序A到系统S
- 定义CmdRequest和CmdResponse
- 初始化时:给定/存储
f
- 初始化时:存储
的初始值(作为不透明值,S无法看到任何a
结构)a
- 调用
(作为系统操作的一部分)f
- 传递它以前存储的值(来自以前的调用或系统初始化)
- 存储返回类型的第一个
组件
- 定义
f
- 初始化时:将
传递到Sf
- 初始化时:将
a的初始值传递给S
- 可以为
使用特定类型(例如Data.HashMap)a
- 当调用
时,它可以看到f
a
State a :: ...
但是有没有办法隐藏a
?(其中“隐藏”表示不将类型参数指定为状态
)
我最初尝试了类型类和存在类型,但被卡住了
A的状态
初始化
. . .
. . .
. . .
| | |
x------通过f------>|
|x-----商店f------>|
x-------a------>|
|x------存储a------->|
| |
. .
. .
. .
系统运行
| |
| |
x------得到f------->|
| |
x------得到一个------->|
| |
||
| | |
. . .
. . .
. . .
| 拥有一个对库不透明但特定于应用程序的函数是Haskell的谋生之道。例如,从“容器”包考虑。
映射,m::map ka
是一段数据,它将键k
与值A
关联起来。Data.Map库不需要知道其值的结构,事实上,在k
和a
中保持Map
多态性的定义会阻止库对键和值的内容做任何事情。嗯,几乎什么都有。typeclas稍微改变了对话,如果用户提供了一个关于键或值的函数,那么它们可以被改变。然而,重要的一点仍然是,多态参数限制了在库中可以做什么
您可以尝试使用存在类型来摆脱多态参数。就GitHub代码而言,您可以使用以下内容定义状态:
{-# Language ExistentialQuantification #-}
import Control.Concurrent.MVar
data State = forall a . State
{
_nextValue :: MVar a
, _applyLogEntry :: a -> CmdRequest -> (a, CmdResponse)
}
其中,为了清晰起见,我已将monad参数从数据状态ma
中删除。值参数a
,现在被存在量化隐藏。检索存储的值成为一个关键点。下面的代码
getStVal :: State -> IO a
getStVal st@(State mva f) = takeMVar mva
抛出编译错误,指出函数的预期类型是IO a
(由于getStVal
签名),但实际类型是IO a1
(由于State
类型)。这是因为getStVal
要求一个特定的(尽管在编译时未知)类型,而数据定义说该值可以是任何类型(但我们不能在编译时假设任何特定类型)
将a
的存储值传递给App也没有帮助,因为App无法根据a的值改变其行为。状态值可以更新,但不能直接检查。(您可以包括typeclass信息,以表明某些方法(例如show)可以应用于该类型。)
您还可以尝试其他扩展。最终,我不确定GADTs或RankNTypes会有更好的表现。您必须以某种方式说服编译器,您的程序将能够使用未知类型。但实际上,多态参数通常是实现这一点的最佳方法 为什么需要做一些特殊的事情?让L的API包含诸如fooWithCallback::(a->CmdRequest->(a,CmdResponse))->{-type of foo actions-}
之类的函数有什么错?foo操作的类型必须有一个a
的参数,但它确实不需要任何技巧或艰苦的工作。它具有类型iorefa->(a->(a,b))->iob
,这与类型(a->()->(a,b))->iorefa->iob
基本相同;眯着眼睛看,你会发现()
是一种(特别无聊的)CmdRequest
,b
是一种CmdResponse
,而IORef a->IO b
是一种foo操作,使其符合我在之前的评论中提出的形状。我看不出有任何问题。请记住,如果定义了f
并且CmdRequest
是
getStVal :: State -> IO a
getStVal st@(State mva f) = takeMVar mva