Как мне развалиться? Может быть, монады в святилище?

Вот простое цепочечное выражение, использующее современный javascript для поиска значения для конкретного ключа, расположенного в строке, содержащей разделенный запятыми список пар ключ-значение, разделенных =,

Это падает, если источник нулевой или ключ не найден, в моей голове это казалось большой задачей для монады Maybe.

// 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"]])

Я не хотел, чтобы это был вопрос "решить эту проблему для меня", но мне трудно передать, что именно мне действительно нужно для помощи.

NB. Я все еще пытаюсь "выяснить" FP, так что это определенно проблема знакомства.

1 ответ

Мы можем использовать 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
]);

Вот альтернатива коду Sanctuary, использующая только современный JavaScript:

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изд.

И ты можешь справиться с { Tags: null }с помощью необязательного оператора цепочки (?.).

Другие вопросы по тегам