В чем разница между обозначениями `let .. in do` и `‹-` в монадах Haskell?

Я пытаюсь реализовать функцию, которая преобразует строку в список Maybe Ints, например. readInts "1 2 42 foo" = [Just 1,Just 2,Just 42,Nothing].

Мой первый подход был:

readInts (s::String) = do {
    ws <- words s;
    return (map (readMaybe::(String -> Maybe Int)) ws)
}

Это привело к следующей ошибке:

lab_monad.hs:20:52:
    Couldn't match type ‘Char’ with ‘[Char]’
    Expected type: [String]
      Actual type: String
    In the second argument of ‘map’, namely ‘ws’
    In the first argument of ‘return’, namely
      ‘(map (readMaybe :: String -> Maybe Int) ws)’
Failed, modules loaded: none.

Далее я попробовал (и сработал):

readInts (s::String) = do {
    let ws = (words s) in do
        return (map (readMaybe::(String -> Maybe Int)) ws)
} 

Мой вопрос здесь в том, что words s, очевидно, имеет тип [String]. Почему переводчик говорит, что это String? Что я не понимаю в операторе <-?


person Tomasz Garbus    schedule 18.03.2018    source источник
comment
Почему вы вообще пытаетесь использовать здесь нотацию do?   -  person Thomas    schedule 18.03.2018
comment
Научиться использовать нотацию do, даже если здесь это не лучшее решение.   -  person Tomasz Garbus    schedule 18.03.2018
comment
@TomaszGarbus do-нотация совершенно не имеет отношения к тому, что вы пытаетесь здесь сделать. Я рекомендую прочитать соответствующую главу LYAH, так как полное объяснение монад и do-нотации слишком широк для одного вопроса.   -  person AJF    schedule 18.03.2018
comment
Это не совсем неважно; это просто неправильное смешивание монадических операций и map.   -  person chepner    schedule 18.03.2018
comment
Потенциально актуально: stackoverflow.com/ вопросы/28624408/   -  person chi    schedule 19.03.2018


Ответы (1)


ws <- words s, в монаде списка, недетерминировано присваивает одно слово от words s до ws; оставшийся код просто работает с этим одним словом, а функция return "волшебным образом" объединяет результаты работы со всеми словами в список результатов.

readInts s = do
   ws <- words s  -- ws represents *each* word in words s
   return (readMaybe ws)

Нотация do — это просто синтаксический сахар для использования монадического bind:

readInts s = words s >>= (\ws -> return (readMaybe ws))

Не используя экземпляр Monad для списков, вы можете использовать map для применения одной и той же функции к каждому слову.

readInts s = map readMaybe (words s)

let, с другой стороны, просто предоставляет имя для более сложного выражения, которое будет использоваться в другом выражении. Его можно считать синтаксическим сахаром для определения и немедленного применения анонимной функции. То есть,

let x = y + z in f x

эквивалентно

(\x -> f x) (y + z)
  ^     ^      ^
  |     |      |
  |     |      RHS of let binding
  |     part after "in"
  LHS of let binding

Оператор let с несколькими привязками эквивалентен вложенным операторам let:

let x = y + z
    a = b + c
in x + a

эквивалентно

let x = y + z
in let a = b + c
   in x + a

какие обесахаривания

(\x -> (\a -> x + a)(b + c))(y + z)
person chepner    schedule 18.03.2018