Functional programming 我该如何在圣殿js中折叠单子

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

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

如果源代码为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