【问题标题】:crockjs and apply functions need a function as valuecrockjs 和 apply 函数需要一个函数作为值
【发布时间】:2018-07-27 12:46:48
【问题描述】:

我实际上是在学习函数式编程,我正在努力学习和使用crockjs

目前,我正在尝试实现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 的运行方式。对我来说,它在我的代码中接受一个函数作为其内部值,但似乎并非如此。

任何人都可以解释并告诉我我在这里做错了什么吗?

感谢您的帮助

【问题讨论】:

    标签: javascript functional-programming


    【解决方案1】:

    欢迎使用 JS 函数式编程,感谢您给 crocks 一个机会。

    在查看那篇文章时,需要注意的一件事是作者介绍了 State ADT 内部的机制如何工作,而不是真正如何使用现有的 State ADT。

    我将提供有关如何手动处理State 事务的说明,这与您在实现中的内容很接近。然后我将简要举例说明如何使用构造助手(如getmodify 来减少VitalForce)用于处理和构建State 事务。

    另外我会简单解释一下Applicatives的使用。

    首先让我们从crocks引入几个ADT

    const Pair = require('crocks/Pair')
    const State = require('crocks/State')
    

    我们需要 State 构造函数来接收一个返回 Pair 的函数(作者在帖子中提到的元组)。构造函数的工作原理可以查看here

    在讨论 State 函数之前,我们需要 LivingPart 函数:

    // LivingPart :: (Integer, String) -> Object
    const LivingPart = (unitOfForce, part) =>
      ({ [part]: { unitOfForce } })
    

    我已经改变了原来的结构,所以我们可以将任何给定的Part 与另一个合并在一起。

    现在我们可以实现makeLiveThing。你在你的实现中几乎已经有了它。这里唯一真正的区别是我们需要用函数构造State ADT 并返回Pair。请注意,我们仍然引入了String,但返回了一个State ADT,它将在调用runWith 时执行。请记住,当前状态将传递给 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.assigncrocks 提供了一个名为 assign 的助手,它可以做到这一点,所以让我们把它引入:

    const assign = require('crocks/helpers/assign')
    

    现在我们有了组合内部值的方法,我们需要将这个函数“提升”到我们的State 类型中,crocks 也有一个可以在Applicatives 上使用的函数来提升和应用ADT (2) 实例的内部值称为liftA2。这意味着“将一个函数提升为具有 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 } )
    

    以下是包含这些功能的一些文档:

    因此,作为旁注,我在on this channel 进行 LiveCode 广播,如果您对此感兴趣,我将阅读这篇博文并讨论如何在 crocks 中实现这一点。

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-27
      • 2014-01-16
      • 1970-01-01
      • 1970-01-01
      • 2014-08-28
      • 2018-08-30
      • 1970-01-01
      相关资源
      最近更新 更多