在Mark Seemann 的博客文章和示例中,我第一次看到了自由 monad 作为构建纯代码和 IO 代码之间界限的一种方式。我的基本理解是,免费的 monad 可以让您构建纯函数的程序(抽象语法树 - AST),然后解释器将其转换为一系列不纯的过程调用。因此,该解释器将 AST 的纯操作转换为一系列 monadic IO 操作。
我想知道这是否是在重复 Haskell 运行时已经对 IO monad 所做的事情。如果我认为 IO 没什么特别的,而是一个常规的 Monad,其绑定函数>>=通过 IO 中的所有 monadic 操作对“真实世界”的状态进行排序,那么这种排序本身不提供任何计算(如在很好的答案在这里)。然后,我可以将所有 IO 操作(如getLine、writeFile等)视为自由 IO monad 中的操作,并将 Haskell 运行时视为解释器。运行时通过一些底层系统调用、C FFI调用等方式来解释每个IO动作,这显然是不纯的。
因此,在这个视图中,返回 IO 操作的函数只是构建 AST,然后由 Haskell 运行时解释。但到目前为止,一切都是纯粹的。在这种观点下,函数a -> IO b不是不纯的,就像在自由 monad 中的操作不是不纯的一样。
这种直觉是否正确?如果没有,它的不足之处在哪里?
自 1.9 以来,我正在尝试在 docker 中使用新命名的卷。它们应该取代仅数据容器。但是,我很难在以下用例中使用命名卷:
我有几个容器以非 root 用户身份运行应用程序,称之为 appuser;我确实修复了我的基础镜像中的 uid 和 gid,所有应用程序容器镜像都来自该镜像。每个应用程序将日志文件写入 appuser 拥有的文件夹。然后,我设置了一个仅用于数据的容器,该容器源自相同的基本映像。使用volumes-from 指令,我挂载这个纯数据容器来存储我的日志文件。它有效,因为 uid 是固定的。
相反,如果我尝试使用命名卷,应用程序将无法写入它们的日志文件,因为命名卷归某些依赖于卷驱动程序(在我的情况下为“本地”)驱动程序的用户所有。是否可以在命名卷中创建具有正确权限的日志文件夹?
或者更一般地说 - 如果挂载命名卷的容器中的应用程序以非 root 用户身份运行,命名卷是否有用?从我上面刚刚描述的内容来看,这样的应用程序将无法写入或创建命名卷中的任何文件夹。
在更大的 Haskell 应用程序中,是否有一个一致的最佳实践来聚合和处理跨多个函数层的类型错误?
来自介绍性文本和Haskell Wiki,我认为纯函数应该是完整的——也就是说,将错误评估为它们的共同域的一部分。运行时异常无法完全避免,而应仅限于 IO 和异步计算。
如何在纯同步函数中构建错误处理?标准建议是使用Either作为返回类型,然后为函数可能导致的错误定义代数数据类型 (ADT)。例如:
data OrderError
= NoLineItems
| DeliveryInPast
| DeliveryMethodUnavailable
mkOrder :: OrderDate -> Customer -> [lineIntem] -> DeliveryInfo -> Either OrderError Order
Run Code Online (Sandbox Code Playgroud)
但是,一旦我尝试将多个产生错误的函数组合在一起,每个函数都有自己的错误类型,我该如何组合错误类型?我想将所有错误聚合到应用程序的 UI 层,在那里解释错误,可能映射到特定于语言环境的错误消息,然后以统一的方式呈现给用户。当然,这种错误呈现不应该干扰应用程序域环中的功能,应该是纯业务逻辑。
我不想定义一个超级类型——一个包含应用程序中所有可能错误的大型 ADT;因为这意味着 (a) 所有域级代码都需要依赖于这种类型,这会破坏所有的模块化,以及 (b) 这将创建对于任何给定函数来说都太大的错误类型。
或者,我可以在每个组合函数中定义一个新的错误类型,然后将各个错误映射到组合错误类型:说funA带有 error-ADT ErrorA,并funB带有ErrorB. 如果funC,有错误类型ErrorC,均适用funA和funB,funC需要所有错误的情况下,从地图ErrorA和ErrorB新的案件是所有部分ErrorC。这似乎是很多样板。
第三个选项可能是funC包装来自funA和的错误funB:
data ErrorC
= …Run Code Online (Sandbox Code Playgroud) 在阅读 Haskell 教科书中关于不同 monad 的章节时,当作者从解释bind和 monad 定律的细节到实际使用 monad时,我反复迷路。突然,诸如“在 monadic 上下文中运行函数”或“运行 monad”之类的表达出现了。同样,在库文档和关于 monad 转换器堆栈的讨论中,我读到一些函数“可以在任何选择的 monad 中运行”的声明。这个“在 monad 中运行”到底是什么意思?
有两件事我似乎不太明白:
return, >>=) 和定律的类型类。因此,在 monad 中“运行”某些东西可能意味着 (a) 将其作为参数提供给return,或 (b) 使用>>=. 如果 monad 是 type m a,那么在 a) 的情况下,某些东西必须是 type a,以匹配return函数的类型。如果 b)某事必须是 type 的函数a -> m b,以匹配函数的类型>>=。由此,我不明白如何在任意 monad 中“运行”某个函数,因为我排序使用的函数>>=都必须具有相同的类型签名,并且我使用的值return必须是特定的 monad 类型参数。run …我是groovy的新手,刚开始探索它的元编程功能.我坚持在bean构造函数调用中添加缺少的属性.
在与FactoryBuilderSupport一起使用的类中,我想动态添加在构造函数调用期间尚未定义和提供的那些属性.这是精简版:
@Canonical
class MyClass {
def startDate
def additionalProperties = [:]
def void propertyMissing(String name, value) {
additionalProperties[name] = value
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我构造具有未知属性的类,则不添加proprty但是我得到了一个MissingPropertyException:
def thing = new MyClass(startDate: DateTime.now(), duration: 1234)
Run Code Online (Sandbox Code Playgroud)
属性持续时间不存在,我预计它将通过处理propertyMissing.据我所知groovy,调用tuple-constructor会导致无参数构造函数调用,然后调用groovy生成的setter.那么为什么我会得到一个MissingPropertyException?
由于我是groovy的新手,我可能缺少一些基本的AST或MOP规则.我非常感谢你的帮助.
通过“Elm in Action”,我了解到要编写测试,某个模块的测试套件中所需的所有函数和类型都必须由该模块公开。这似乎破坏了封装。我不想公开内部函数和类型构造函数,这些函数和类型构造函数应该保持隐藏状态,只是为了使它们可测试。有没有办法公开内部函数和类型仅用于测试,而不用于常规使用?
我是《绿箭侠》的新手,并尝试建立其效果系统如何工作的思维模型;特别是它如何利用 Kotlin 的suspend系统。我非常模糊的理解如下;如果有人可以确认、澄清或纠正它,那就太好了:
由于 Kotlin 不支持更高种类的类型,因此将应用程序和 monad 实现为类型类非常麻烦。相反,Arrow 从 Kotlin 的挂起机制提供的延续原语中派生出所有 Arrow 的单子类型的单子功能(绑定和返回)。这是正确的吗?特别是,短路行为(例如, fornullable或either)以某种方式实现为定界延续。我不太明白 Kotlin 挂起机制的哪个特定功能在这里发挥作用。
如果上述内容大致正确,那么我有两个后续问题:我应该如何包含非 IO 单子操作的范围?举一个简单的对象构造和验证示例:
suspend fun mkMessage(msgType: String, appRef: String, pId: String): Message? = nullable {
val type = MessageType.mkMessageType(msgType).bind()
val ref = ApplRefe.mkAppRef((appRef)).bind()
val id = Id.mkId(pId).bind()
Message(type, ref, id)
}
Run Code Online (Sandbox Code Playgroud)
在 Haskell 的 do 表示法中,这将是
mkMessage :: String -> String -> String -> Maybe Message
mkMessage msgType appRef pId = do
type <- mkMessageType msgType
ref <- …Run Code Online (Sandbox Code Playgroud) 我正在尝试新的 docker 网络功能。我使用容器链接将旧设置迁移到新的桥接网络;到目前为止,我已经在同一主机上的多个容器之间启动并运行了专用桥接网络。
现在我正在寻找一种方法来复制同一容器的多个链接别名。
假设我有一个名为 的容器myBox加入了 docker 网络。我希望有相同的容器也可以访问myServer。
使用链接,我只需设置两个具有不同别名的链接。
有没有办法使用 docker 网络来实现这一点?
对于某些文件操作,我需要检查文件是否存在,是否已被修改,然后才对其进行一些操作。我的新手 Haskell 代码如下(简化):
someFileOp ::FileContents -> FilePath -> IO (FileOpResult)
someFileOp contents absFilePath = do
fileExists <- DIR.doesFileExist absFilePath
if fileExists
then do
isMod <- isModified contents absFilePath
if isMod
then return FileModified
else return $ doSomethingWithFile
else return FileNotFound
Run Code Online (Sandbox Code Playgroud)
它确实有效。但是,嵌套的 if 表达式在我看来是错误的- 不像 FP。检查 IO 中的几个布尔条件然后根据它们的结果采取一些行动的惯用方法是什么?
我正在开发一个中型 Kotlin 项目,我需要通过纯函数的许多嵌套调用来线程化从文件读取的配置信息。对于 Reader monad 来说,这似乎是一个明显的例子。但是,我还没有弄清楚如何在 Kotlin 中有效地实现 Reader。
我正在使用 Arrow 库 (v1.1.3),但令我惊讶的是,它没有附带 Reader 的实现。使用 Arrow 通过函数调用来线程化配置数据的首选方法是什么?由于 Arrow 已转向使用 Kotlin 的本机挂起系统来理解 monad,我认为这意味着不需要专门的 Reader 实现。该怎么做呢?
我试图从第一原则来理解函数式编程,但我仍然停留在纯函数世界和具有状态和副作用的不纯现实世界之间的接口上。从数学的角度来看,
详细说明:在我的理解中,纯函数是从域到共域的映射。最终,它是从计算机内存中的某些值到内存中的某些其他值的映射。在函数式语言中,函数是声明式定义的;即,它们描述了映射,但不描述需要对特定输入值执行的实际计算;后者由编译器来推导。在具有空闲内存的简化设置中,运行时将没有计算;相反,编译器可以在编译时为每个函数创建一个查找表。执行一个纯程序相当于查表。因此,组合函数相当于构建更高维的查找表。当然,拥有计算机的全部意义在于设计出无需逐点查找表即可指定函数的方法 - 但我发现心智模型有助于区分纯函数和效果。但是,我很难将这种心智模型应用于高阶函数:
现在到令人讨厌的现实世界。与它的交互并不纯粹,但没有它,就没有合理的程序。在我上面的简化心智模型中,分离程序的纯部分和不纯部分意味着每个函数式程序的基础是一层命令式语句,这些语句从现实世界中获取数据,对其应用纯函数(进行查表),以及然后将结果写回现实世界(磁盘、屏幕、网络等)。
在 Haskell 中,这种与现实世界的命令式交互被抽象为IO 操作,编译器根据它们的数据依赖性对它们进行排序。但是,我们不会直接将程序编写为一系列命令式 IO 操作。相反,有些函数会返回 IO 操作(类型为 的函数:: IO a)。但据我所知,这些不可能是真正的功能。这些是什么?如何根据上面概述的心智模型最好地思考它们?
io haskell functional-programming purely-functional higher-order-functions
我需要验证数字输入是否在某个范围内。为此,我正在使用
ensure :: (a -> Bool) -> a -> Maybe a
ensure p v | p v = Just v
| otherwise = Nothing
Run Code Online (Sandbox Code Playgroud)
检查某个值的上限和下限的一种方法x :: Int是通过一元链:
let validated = pure x >>= ensure (>0) >>= ensure (<100)
Run Code Online (Sandbox Code Playgroud)
据我了解,两次验证的顺序无关紧要;因此,应该可以以应用形式重写上述表达式。如何?我没能做到,但我希望一旦我做到了,我就能对应用程序有更深入的了解:-)。
haskell ×6
monads ×4
arrow-kt ×2
docker ×2
io ×2
kotlin ×2
applicative ×1
containers ×1
elm ×1
elm-test ×1
exception ×1
free-monad ×1
groovy ×1
io-monad ×1
mop ×1
networking ×1
predicate ×1
reader-monad ×1
suspend ×1
validation ×1