Подтвердить что ты не робот

Каков тип переменной в do-notation здесь, в Haskell?

Коды ниже выглядят совершенно ясно:

do 
  x <- Just 3
  y <- Just "!"
  Just (show x ++ y)

Здесь тип x равен Num, а y - String. (<- здесь используется для получения фактического значения из Monad)

Однако этот сниппет выглядит не так ясно для меня:

import Control.Monad.Instances
addStuff :: Int -> Int
addStuff = do
  a <- (* 2)
  b <- (+ 10)
  return (a + b)

Каков тип a и тип b здесь? Кажется, они действуют как Num, но a <- (* 2) и b <- (+ 10) выглядят загадочными здесь...

Есть ли у кого-нибудь идеи об этом?

4b9b3361

Ответ 1

Ну, ты наткнулся на какую-то странную монаду.

Предполагаемая монада - это Monad ((->) r). Теперь, что это значит? Ну, это монада функций вида r -> *. I.e. функций, которые принимают один и тот же тип ввода.

Вы спросили, что такое тип a и b в этом случае. Ну, они оба Num a => a, но это не очень многое объясняет.

Интуитивно мы можем понять монаду следующим образом: Монадическое значение - это функция, которая принимает значение типа r в качестве входного. Всякий раз, когда мы связываем в монаде, мы берем это значение и передаем его связанной функции.

Т.е. в нашем примере addStuff, если мы назовем addStuff 5, то a привязано к (*2) 5 (которое равно 10), а b привязано к (+10) 5 (что 15).

Посмотрите более простой пример из этой монады, чтобы попытаться понять, как это работает:

mutate = do a <- (*2)
            return (a + 5)

Если мы отбросим это на привязку, получим:

mutate = (*2) >>= (\a -> return (a + 5))

Теперь это не очень помогает, поэтому используйте определение bind для этой монады:

mutate = \ r -> (\a -> return (a + 5)) ((*2) r) r

Это сводится к

mutate = \ r -> return ((r*2) + 5) r

Мы используем определение return const, можем уменьшить до

mutate = \ r -> (r*2) + 5

Какая функция, которая умножает число на 2, а затем добавляет 5.

Эта странная монада.

Ответ 2

Учитывая addStuff

addStuff :: Int -> Int
addStuff = do
  a<-(*2)
  b<-(+10)
  return (a+b)

определение desugars в

addStuff = 
    (* 2) >>= \a -> 
        (+ 10) >>= \b -> 
            return (a + b)

Наведите указатель мыши на >>= в fpcomplete онлайн-редактор.

:: Monad m => forall a b. 
              (m a       ) -> (a   -> m b       ) -> (m b       )
::            (Int -> a  ) -> (a   -> Int -> b  ) -> (Int -> b  )
::            (Int -> Int) -> (Int -> Int -> Int) -> (Int -> Int)

Это заставляет нас думать, что мы используем экземпляр Monad для функций. Действительно, если мы посмотрим на исходный код, мы увидим

instance Monad ((->) r) where
    return = const
    f >>= k = \ r -> k (f r) r

Используя эту недавно полученную информацию, мы можем сами оценить функцию addStuff.

Учитывая начальное выражение

(* 2) >>= ( \a -> (+10) >>= \b -> return (a + b) )

подставляем с помощью определения >>=, давая нам (в следующих {}, [], () просто иллюстрируем различную глубину ())

\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {(* 2) r1} r1

упростить второй-последний термин внутри самой внешней лямбда

\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {r1 * 2} r1

применить {r1 * 2} к {\a -> ...}

\r1 -> {(+10) >>= \b -> return ((r1 * 2) + b)} r1 

замените оставшийся >>= своим определением снова

\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [(+10) r2] r2} r1

упростить второй-последний термин внутри внутренней лямбда

\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [r2 + 10] r2} r1

применить [r2 + 10] к {\b -> ...}

\r1 -> {\r2 -> [return (r1 * 2 + (r2 + 10))] r2} r1

примените r1 к {\r2 -> ...}

\r1 -> {return (r1 * 2 + r1 + 10) r1}

замените return своим определением

\r1 -> {const (r1 * 2 + r1 + 10) r1}

оцените const x _ = x

\r1 -> {r1 * 2 + r1 + 10}

приукрасить

\x -> 3 * x + 10

наконец, получим

addStuff :: Int -> Int
addStuff = (+ 10) . (* 3)