我如何在庇护所 js 中折叠 Maybe monads
How do I collapse Maybe monads in sanctuary js
这是一个简单的链式表达式,使用现代 javascript 来查找位于字符串中的特定键的值,该字符串包含由 =
分隔的键值对的逗号分隔列表。
如果源为空或找不到密钥,这将失败,在我看来,这对于 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 安装了 sanctuary 并开始尝试功能性解决方案。这是我到目前为止所得到的,而且看起来很丑陋,这告诉我我一定是做错了什么或使用了错误的工具。
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"]])
我不希望这是一个 "solve this problem for me" 问题,但我很难表达我真正需要帮助的地方。
N.B。我还在尝试到"figure out" FP,所以这肯定是熟度问题。
我们可以使用 S.map
to transform inner values and 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
到 select 第一场比赛的更新版本:
// 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
]);
这是仅使用现代 JavaScript 的 Sanctuary 代码的替代方法:
const stripPrefix = e =>
e.startsWith(`${tag}=`)
&& e.replace(`${tag}=`, "")
const getTag = tag => product =>
product?.Tags
?.split(",")
.map(stripPrefix)
.filter(Boolean)
[0]
|| null
getTag("b")({ Tags: "a=y,b=c" }) // returns 'y'
getTag("z")({ Tags: "a=y,b=z" }) // returns null
getTag("a")({ Tags: null }) // returns null
stripPrefix
函数 returns false
如果未找到标签,则 filter
ed.
并且您可以使用 optional chaining 运算符 (?.
) 来处理 { Tags: null }
。
这是一个简单的链式表达式,使用现代 javascript 来查找位于字符串中的特定键的值,该字符串包含由 =
分隔的键值对的逗号分隔列表。
如果源为空或找不到密钥,这将失败,在我看来,这对于 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 安装了 sanctuary 并开始尝试功能性解决方案。这是我到目前为止所得到的,而且看起来很丑陋,这告诉我我一定是做错了什么或使用了错误的工具。
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"]])
我不希望这是一个 "solve this problem for me" 问题,但我很难表达我真正需要帮助的地方。
N.B。我还在尝试到"figure out" FP,所以这肯定是熟度问题。
我们可以使用 S.map
to transform inner values and 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
到 select 第一场比赛的更新版本:
// 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
]);
这是仅使用现代 JavaScript 的 Sanctuary 代码的替代方法:
const stripPrefix = e =>
e.startsWith(`${tag}=`)
&& e.replace(`${tag}=`, "")
const getTag = tag => product =>
product?.Tags
?.split(",")
.map(stripPrefix)
.filter(Boolean)
[0]
|| null
getTag("b")({ Tags: "a=y,b=c" }) // returns 'y'
getTag("z")({ Tags: "a=y,b=z" }) // returns null
getTag("a")({ Tags: null }) // returns null
stripPrefix
函数 returns false
如果未找到标签,则 filter
ed.
并且您可以使用 optional chaining 运算符 (?.
) 来处理 { Tags: null }
。