我实际上正在学习函数式编程,我正在努力学习和使用金银花
现在,我正在尝试实现https://fsharpforfunandprofit.com/monadster/中描述的"monadster“程序。
这是我现在所拥有的(仅仅是开始)
const State = require("crocks/State");
const LivingPart = (unitOfForce, deadThing) => ({ unitOfForce, deadThing });
// Creating the potential living thing
const makeLiveThingM = deadThing => {
const becomeAlive = vitalForce => {
const unitOfForce = 1;
const remaining = vitalForce - unitOfForce;
return { part: LivingPart(unitOfForce, deadThing), remaining };
};
return State.get(becomeAlive);
};
// Using containers
const deadLegM = makeLiveThingM("deadLeg");
const deadArmM = makeLiveThingM("deadArm");
const livingThings = deadLegM.ap(deadArmM).evalWith(1);
console.log(livingThings);我的问题是,它引发了以下错误:
/Users/pc/Soft/experiments/functional/crocks/node_modules/crocks/State/index.js:101
throw new TypeError('State.ap: Source value must be a function')
^
TypeError: State.ap: Source value must be a function据我所见,这可能是因为我不了解apply函数,也不了解State.get的运行方式。对我来说,它在代码中接受一个函数作为它的内部值,但看起来并非如此。
有人能解释并告诉我我在这里做错了什么吗?
谢谢你的帮忙
发布于 2018-07-27 04:41:27
欢迎使用JS进行函数式编程,并感谢您给crocks一次机会。
在看这篇文章时,需要注意的一点是,作者介绍的是State ADT内部的机制是如何工作的,而不是实际上如何使用现有的State ADT。
我将解释如何手动处理State事务,这与您在实现中的功能非常接近。然后,我将给出一个简短的示例,说明如何使用构造助手(比如get和modify来减少VitalForce)来处理和构建State事务。
此外,我还将简要解释如何使用Applicatives。
因此,首先,让我们从crocks引入几个ADT
const Pair = require('crocks/Pair')
const State = require('crocks/State')我们需要状态构造函数来接受一个函数,它将返回一个Pair (作者在文章中提到的元组)。构造函数的工作原理可以找到这里。
在讨论状态函数之前,我们需要这个LivingPart函数:
// LivingPart :: (Integer, String) -> Object
const LivingPart = (unitOfForce, part) =>
({ [part]: { unitOfForce } })我已经改变了原来的结构,这样我们就可以将任何给定的Part合并到另一个。
有了这一点,我们就可以实现makeLiveThing了。在你的实施过程中你已经有了很多。这里唯一真正的区别是我们需要用函数构造State ADT并返回Pair。注意,我们仍然引入了String,但是返回一个runWith调用时将执行的runWith ADT。请记住,当前状态将传递给State实例封装的函数(在本例中,vitalForce是我们的状态):
// makeLiveThing :: String -> State Integer Object
const makeLiveThing = part => State(
vitalForce => {
const unitOfForce = 1
const remaining = vitalForce - unitOfForce
return Pair(
LivingPart(unitOfForce, part),
remaining
)
}
)现在我们已经有了创建LivingPart和处理状态事务(减少1)的方法,我们可以创建以下几个部分:
// rightLeg :: State Integer Object
const rightLeg =
makeLiveThing('right-leg')
// leftArm :: State Integer Object
const leftArm =
makeLiveThing('left-arm')现在,连接这些State实例的任务来了。使用apply是正确的,因为当一个ADT同时拥有一个ap方法和一个of方法时,它被称为Applicative。当我们有一个Applicative时,我们可以认为该类型能够组合(2)不依赖于另一个实例的独立实例。我们只需要提供一种方法来告诉类型如何组合它。
通常,这是通过一个可以对ADT中包含的类型起作用的函数来完成的。在我们的示例中,它是一个Object,因此组合(2)对象的一种方法是使用Object.assign。crocks提供了一个名为assign的助手,它可以做到这一点,所以让我们将其引入:
const assign = require('crocks/helpers/assign')现在我们有了组合内部值的方法,我们需要将这个函数“提升”到我们的State类型中,crocks还有一个函数可以在Applicatives上使用来提升和应用名为liftA2的ADT (2)实例的内部值。意思是“用两个实例将一个函数提升到一个应用程序中”。
因此,让我们把它也带来,然后创建一个函数,将用于连接(2) Parts
const liftA2 = require('crocks/helpers/liftA2')
// joinParts :: Applicative m => m Object -> m Object -> m Object
const joinParts =
liftA2(assign)现在使用这个函数,我们可以提升和连接这些部分,并使用我们的VitalForce运行结果
joinParts(rightLeg, leftArm)
.runWith(10)
//=> Pair( { left-arm: { unitOfForce: 1 }, right-leg: { unitOfForce: 1 } }, 8 )注意,结果在左边(组合的活部分)和右边的状态(剩余的VitalForce)中有结果。
以下是对上述内容的一些参考:
现在,我将展示一个简单的示例,说明如何设置单个State事务,以便从池中获取VitalForce。我不打算在这里详细解释,但是您应该能够在这个示例和State文档之间收集一些信息:
const State = require('crocks/State')
const constant = require('crocks/combinators/constant')
const mapProps = require('crocks/helpers/mapProps')
const { modify } = State
// decrementBy :: Integer -> Integer -> Integer
const decrementBy =
x => y => y - x
// VitalForce :: { units: Integer }
// decUnitsBy :: Integer -> VitalForce -> VitalForce
const decUnitsBy = units =>
mapProps({ units: decrementBy(units) })
// getVitalForce :: Integer -> State VitalForce VitalForce
const getVitalForce = units =>
modify(decUnitsBy(units))
.map(constant({ units }))
getVitalForce(3)
.runWith({ units: 10 })
//=> Pair( { units: 3 }, { units: 7 } )下面是这些包含的函数的一些文档:
因此,作为附带说明,我做一个LiveCode广播在这个频道,我将浏览这篇博客文章,并讨论如何在crocks中实现这一点,如果这是您感兴趣的事情的话。
希望这能帮上忙!
https://stackoverflow.com/questions/51531825
复制相似问题