functional-programming - 我如何在庇护所js中折叠也许单子(monad)

标签 functional-programming sanctuary

这是一个使用现代 JavaScript 的简单链式表达式,用于查找字符串中特定键的值,该字符串包含以 = 分隔的逗号分隔键值对列表。

如果源为空或未找到 key ,这就会失败,在我看来,这对于 Maybe monad 来说似乎是一项伟大的任务。

// Grab the tag with key in `tag`
const getTag = (product, tag) =>
  product.Tags
    .split(',')
    .find(t => t.startsWith(`${tag}=`))
    .split('=')[1]

getTag({Tags: 'a=y,b=z'}, 'a') // returns 'y'
getTag({Tags: 'a=y,b=z'}, 'z') // returns boom (desired null)
getTag({Tags: null}, 'a') // returns boom (desired null)

所以我 npm 安装了 saintuary 并开始使用功能解决方案。这是我到目前为止所得到的,感觉它非常丑陋,这告诉我我一定做错了什么或使用了错误的工具。

const getk = S.map(S.filter(S.test(/=/)))(S.splitOn(','))

S.map(S.map(S.map(S.splitOn('='))))(S.map(getk))(S.toMaybe(null))
// Nothing
S.map(S.map(S.map(S.splitOn('='))))(S.map(getk))(S.toMaybe('a=y,b=z'))
//Just ([["a", "y"], ["b", "z"]])

我不希望这是一个“为我解决这个问题”的问题,但我很难表达我真正需要帮助的是什么。

注意我仍在尝试“找出”FP,所以这绝对是一个熟悉度问题。

最佳答案

我们可以使用S.map转换内部值和 S.join删除不需要的嵌套:

const S = require ('sanctuary');
const $ = require ('sanctuary-def');

//    getTag :: String -> Object -> Maybe String
const getTag = tag => S.pipe ([
  S.get (S.is ($.String)) ('Tags'),             // :: Maybe String
  S.map (S.splitOn (',')),                      // :: Maybe (Array String)
  S.map (S.map (S.stripPrefix (tag + '='))),    // :: Maybe (Array (Maybe String))
  S.map (S.head),                               // :: Maybe (Maybe (Maybe String))
  S.join,                                       // :: Maybe (Maybe String)
  S.join,                                       // :: Maybe String
]);

getTag ('a') ({Tags: 'a=y,b=z'});   // => Just ('y')
getTag ('z') ({Tags: 'a=y,b=z'});   // => Nothing
getTag ('z') ({Tags: null});        // => Nothing

S.map 后跟 S.join 始终相当于 S.chain :

//    getTag :: String -> Object -> Maybe String
const getTag = tag => S.pipe ([
  S.get (S.is ($.String)) ('Tags'),             // :: Maybe String
  S.map (S.splitOn (',')),                      // :: Maybe (Array String)
  S.map (S.map (S.stripPrefix (tag + '='))),    // :: Maybe (Array (Maybe String))
  S.chain (S.head),                             // :: Maybe (Maybe String)
  S.join,                                       // :: Maybe String
]);

这种方法做了一些不必要的工作,不是短路,而是 S.stripPrefix允许我们一步检查标签是否存在,如果存在则提取其值。 :)

更新版本使用 S.justs选择第一个匹配项:

//    getTag :: String -> Object -> Maybe String
const getTag = tag => S.pipe ([
  S.get (S.is ($.String)) ('Tags'),             // :: Maybe String
  S.map (S.splitOn (',')),                      // :: Maybe (Array String)
  S.map (S.map (S.stripPrefix (tag + '='))),    // :: Maybe (Array (Maybe String))
  S.map (S.justs),                              // :: Maybe (Array String)
  S.chain (S.head),                             // :: Maybe String
]);

关于functional-programming - 我如何在庇护所js中折叠也许单子(monad),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51795210/

相关文章:

functional-programming - 我应该如何映射可能列表?

javascript - Curry 在 Sanctuary 和 Ramda 之间有何不同?

functional-programming - 对 Substitution/`ap` 类型签名和不同实现的理解困惑(函数式编程)

scala - 如何处理 Scala 中的列表选项?

functional-programming - 在匹配中折叠引用会导致生命周期错误

haskell - 在 Haskell 中编写一个函数来获取大小为 n 的所有子序列?

Scala 将列表元素映射到从先前元素计算的值

oop - 在函数式编程中实现多态性

javascript - 使用 Sanctuary 从具有特定 RecordType 的对象中选取字段