如何在圣所js中折叠也许是monads

时间:2018-08-10 23:43:21

标签: functional-programming sanctuary

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

如果源为null或未找到密钥,这会下降,在我看来,这对于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安装了避难所,并开始使用功能性解决方案。据我所知,这是很丑陋的,它告诉我我一定做错了什么或使用了错误的工具。

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,所以这肯定是一个熟悉的问题。

2 个答案:

答案 0 :(得分:1)

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

func main() {

    fs := http.FileServer(http.Dir("static"))
    http.Handle("/", fs)

    bs := http.FileServer(http.Dir("public"))
    http.Handle("/public/", http.StripPrefix("/public/", bs))

    http.HandleFunc("/show", show)
    http.HandleFunc("/db", getDB)

    r := mux.NewRouter()
    r.HandleFunc("/loc/{id}", getLoc)

    log.Println("Listening 3000")
    if err := http.ListenAndServe(":3000", nil); err != nil {
        panic(err)
    }
}

func getLoc(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, http.StatusText(405), http.StatusMethodNotAllowed)
        return
    }
    id := r
    fmt.Println("URL")
    fmt.Println(id)
}

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.chain

S.join

此方法通过不短路来完成一些不必要的工作,但是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.chain (S.head),                             // :: Maybe (Maybe String)
  S.join,                                       // :: Maybe String
]);

答案 1 :(得分:0)

这里是仅使用现代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函数将返回false,然后将其filter编辑。

您可以使用optional chaining运算符({ Tags: null })处理?.