Functional programming 我该如何在圣殿js中折叠单子
下面是一个简单的链式表达式,使用现代javascript查找位于字符串中的特定键的值,该字符串包含由Functional programming 我该如何在圣殿js中折叠单子,functional-programming,sanctuary,Functional Programming,Sanctuary,下面是一个简单的链式表达式,使用现代javascript查找位于字符串中的特定键的值,该字符串包含由=分隔的以逗号分隔的键值对列表 如果源代码为null或找不到密钥,则会出现这种情况,在我看来,这对于Maybe monad来说似乎是一项伟大的任务 //用key in`tag抓取标签` const getTag=(产品、标签)=> 产品标签 .split(“,”) .find(t=>t.startsWith(`${tag}=`)) .split('=')[1] getTag({Tags:'a=y
=
分隔的以逗号分隔的键值对列表
如果源代码为null或找不到密钥,则会出现这种情况,在我看来,这对于Maybe monad来说似乎是一项伟大的任务
//用key in`tag抓取标签`
const getTag=(产品、标签)=>
产品标签
.split(“,”)
.find(t=>t.startsWith(`${tag}=`))
.split('=')[1]
getTag({Tags:'a=y,b=z'},'a')//返回'y'
getTag({Tags:'a=y,b=z'},'z')//返回boom(所需的null)
getTag({Tags:null},'a')//返回boom(所需的null)
我们可以用来转换内部值和删除不需要的嵌套:
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
始终等同于:
这种方法通过不短路来做一些不必要的工作,但允许我们在一个步骤中检查标记是否存在,如果存在,则提取其值。:)
用于选择第一个匹配项的更新版本:
// 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的保护区代码的替代方案: 如果找不到标记,
stripPrefix
函数返回false
,然后获取filter
ed
您可以使用操作符(
?。
)处理{Tags:null}
好的,我想知道管道是否是一个常见的习语。感谢您的分析,这是一次非常有趣且信息丰富的经历。我喜欢这里的许多小细节:使用谓词标记(原始参数顺序是向后的),使用管道执行串行操作(而不是“嵌套”),返回可能的字符串
(函数外的转换对于重用来说更有意义),使用get
来nab可为空/未定义的对象道具(使用toMaybe
feeld强力将数据输入到“monad空间”),以及stripPrefix
优化(使用可能会在管道中产生巨大效果的字符串return)。所有这些加上chain
和join
大大降低了复杂性。您是否经常这样在函数管道旁边编写分步类型信息?我认为head
的工作方式与您预期的不同。getTag('b',{Tags:'a=y,b=z'})//Nothing
我最初编写函数时使用了S.justs
,但一定是把重构搞砸了。我在答案中添加了一个有效的实现。至于类型签名,我经常注释函数,但很少注释管道中的每个步骤(尽管我喜欢在演示幻灯片和堆栈溢出的答案中这样做)。简短而甜美。这再次证明,精明的JS开发人员不需要外来的约束库来进行函数式编程,他们希望有本地类型可以避免设计错误。Lukas,你介意联系一下吗?我有一些普通的JS FP项目,你可以成为一个很好的合作伙伴来激发想法。请联系我。
// 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
]);
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