Haskell: Как се изчислява тази формула? `(върни (+1)) (Само 10) 10`

Става дума за комбиниране на два прости аритметични оператора (две частични функции) от Applicative и Monad. Дотук го разбрах приблизително.

-- ┌──────────────────────────────────────┐  
-- | instance Applicative ((->) r) where  |                                           
-- |   pure = const                       |                                           
-- |   (<*>) f g x = f x (g x)            |  (1)                                          
-- |   liftA2 q f g x = q (f x) (g x)     |                                         
-- |                                      |       
-- | instance Monad ((->) r) where        |                                           
-- |   f >>= k = \r -> k (f r) r          |  (2)                                                                         
-- └──────────────────────────────────────┘

λ> (+) <*> (*2) $ 10  
  -- Type check: 
  (<*>) ::  f     (a -> b) -> f      a  -> f      b
           (-> r) (a -> b) -> (-> r) a  -> (-> r) b
           (r ->   a -> b) -> (r ->  a) -> (r ->  b)
           ---------------    ---------
           (+) ~ f            (*2) ~ g      r ~ x (10)

  -- Actual calcuation: by (1)
  f x (g x) = (+) 10 ((*2) 10) = (+) 10 20 = 30 

λ> (*2) >>= (+) $ 10  -- 30
  -- Type check: 
  (>>=) ::  m     a    ->  a  -> m     b  ->  m     b
           (-> r) a    -> (-> r) a  -> b  -> (-> r) b
           (r  -> a)   -> (a  -> r  -> b) -> (r ->  b)
           ---------      ---------------
           (*2) ~ f       (+) ~ k             r ~ 10

  -- Actual calculation: by (2)
  k (f r) r = (+) ((*2) 10) 10 = (+) 20 10 = 30 

Но когато се опитах да приложа тези неща към някаква структура (Може би), заседнах в нея. (Отбелязах „ТУК“ в края на редовете, където заседнах.)

-- ┌────────────────────────────────────────────────┐   
-- | instance Applicative Maybe where               |                                     
-- |     pure = Just                                |                    
-- |     Just f  <*> m       = fmap f m             |                                       
-- |     Nothing <*> _m      = Nothing              |                                      
-- |     liftA2 f (Just x) (Just y) = Just (f x y)  |                                                  
-- |     liftA2 _ _ _ = Nothing                     |                               
-- |     Just _m1 *> m2      = m2                   |                                 
-- |     Nothing  *> _m2     = Nothing              |                                      
-- |                                                |     
-- | instance  Monad Maybe  where                   |                                 
-- |     (Just x) >>= k      = k x                  |                                  
-- |     Nothing  >>= _      = Nothing              |                                      
-- |     (>>) = (*>)                                |                                                            
-- └────────────────────────────────────────────────┘

λ> Just 10 >>= return . (+1)   -- Just 11
  -- Type check:
  (>>=) ::  m     a    ->  a  -> m     b  ->  m     b
            ----------     --------------
            Just 10        return . (+1) :: a -> m b  
        so, m ~ Maybe, a ~ Int
  Just 10 >>= return . (+1) :: m     b 
                               Maybe Int 
  
  -- Actual calculation:
  Just 10 >>= return . (+1) = return . (+1) $ 10
                            = Just   . (+1) $ 10
                            = Just 11

λ> Just >>= return (+1) $ 10   -- 11
  -- This is point-free style
     I don't get it.
  -- Type check:
  (>>=) ::  m     a    ->     a  -> m     b  ->  m     b
            ----------        --------------
            (-> a) (Maybe a)  m (a -> a)   <==== HERE! 
                                           I can't derive the correct type. 

  -- Actual calculation:                   <==== and HERE!
  m >>= f   = \x -> f (m x) x              <==== How can this formula be derived?
            = (return (+1)) (Just 10) 10   <==== How can this be calculated?

В изразяването на Monad има стил без точки. не го разбирам Как мога да изведа типа и да получа резултата, както в предишните случаи на прости аритметични изрази?

Много благодаря.


Благодаря за страхотния отговор, Нил. С ваша помощ можех да открия какво не е наред в мисълта и кода ми. Но все още не мога да разбера правилно крайния тип на Just >>= return (+1). Актуализирах въпроса си и се опитах да разбера вида му. Знам, че греша в извода си за тип. Мога ли да получа още помощ, за да намеря грешната част и да я поправя?

-- Type check: Incorrect
(>>=) ::  m     a    ->     a  ->  m    b  ->  m     b
         (-> r) a    ->    (-> r)  a -> b  -> (-> r) b
         --------          --------------
         f                 k                  \r -> k (f r) r
         m                 f                  \r -> m (f r) r          
         (-> d) (Maybe d)  (-> r) (n -> n)
     so, a ~ Maybe d
         a ~ n, b ~ n  -- This means a, b, n are all the same type.
                       -- Character a, b can be interchangeable. 
Just >>= return (+1) :: (-> r) b  
                      = (-> r) a          -- by `a ~ b`
                      = (-> r) (Maybe d)  -- by `a ~ Maybe d` 
                                          -- HERE: Is this right?  
                                             Should this be `(-> r) n`?
                      = a -> Maybe b      -- by changing characters
               HERE: WRONG RESULT TYPE??? It must be `a -> a` not `a -> Maybe a` 

-- Actual calcuation:
Moand instance for function (`(-> r)`) type here (I got)
m >>= f = \x -> f (m x) x
        = return (+1) (Just 10) 10
        = return (+1) (Just 10) $ 10
        = const  (+1) (Just 10) $ 10  -- by (1), pure = const
        = (+1) $ 10                   -- 'Just 10' was ignored by 'const (pure)' function.
        = (+) 10 = 11

Много благодаря.


person Larynx    schedule 28.10.2020    source източник


Отговори (1)


In

Just >>= return (+1) $ 10

Just е функция r -> Maybe r, така че се използва функционалната монада ((->) r), даваща редукцията като

return (1+) (Just 10) 10

защото това е определението на >>= за монадата ((->) r) (точно както го дадохте в началото на публикацията си, f >>= k = \r -> k (f r) r).

Сега return (1+) се прилага към Just 10, така че е също функция,

return :: Monad m => a          -> m        a
(1+) :: Num m =>   n -> n
return (1+) :: (Monad m, Num n) => m     (n -> n)
                         Num n  => (r -> (n -> n))
Just 10 :: (Num i => Maybe i) ~     r

За функции, return == const, така че имаме

const (1+) (Just 10) 10
===
(1+) 10
===
11

Другият ви въпрос е как така монадата ((->) r), m >>= f = \x -> f (m x) x? (забелязахте, че е същото като тази дефиниция в горната част, f >>= k = \r -> k (f r) r, до преименуване на някои променливи, нали?)

И отговорът е, защото типовете отговарят:

(>>=)    ::  m     a  -> (a -> m     b ) -> m     b
         ~   (r -> a) -> (a -> (r -> b)) -> (r -> b)
(>>=)        m           f                   x  = b
    where
        mx = m x       :: a
        f_mx = f mx    ::       r -> b
        b = f_mx x     ::            b

редактиране: Нека се опитаме да извлечем типа Just >>= return (+1) без никакво мислене, просто по чисто механичен начин, както би направил компилаторът. Ще използваме основното правило за извличане на типове, съответстващо на правилото Modus Ponens в логиката:

        A     :: a
     F        :: t -> b
    --------------------
     F  A     ::      b       ,  t ~ a

Номерът е да използваме всички променливи от различни типове от самото начало, така че дори няма да имаме шанс да ги смесим:

 -- Just >>= return (+1)  ===  (>>=) Just (return (+1))

return      ::  Monad f =>           d    -> f    d
       (+1) ::           Num n =>  n -> n
                         ----------------
return (+1) :: (Monad f, Num n) =>           f (n ->  n)
---------------------------------------------------------
(>>=)  ::    Monad m => m        a -> (a    -> m      b ) -> m      b
      Just  ::          (->) c (Maybe c)
                        ----------------
                 m ~ (->) c , a ~ Maybe c
                 --------------------------
(>>=) Just  ::   Monad ((->) c) => (Maybe c -> (->) c b ) -> (->) c b
             ~   Monad ((->) c) => (Maybe c -> (c ->  b)) -> (c ->  b)
                                   ---------------------- 
return (+1) :: (Monad f, Num n) =>           f (n ->  n)
                                   ---------------------- 
                          f ~ (->) (Maybe c) , (n ->  n) ~
                                               (c ->  b)
(>>=) Just (return (1+)) ::
      (Monad ((->) c), Monad ((->) (Maybe c))  =>             c ->  b
                      ~  Num n                 =>             n ->  n
person Will Ness    schedule 28.10.2020
comment
Благодаря за страхотния отговор. С твоя помощ можех да намеря грешната част в мисълта си. Трябваше да се позова на екземпляра на монадата за function, а не за Maybe. - person Larynx; 29.10.2020
comment
Актуализирах въпроса си. Последната част от отговора ви (>>=) определено е правилна и я разбрах. Но все още не мога да извлека окончателния тип Just >>= return (+1) :: Num a => a -> a, започвайки от типа (>>=) :: m a -> (a -> m b) -> m b. Знам, че m a трябва да бъде r -> Maybe r или (-> r) (Maybe r). Знам също, че a -> m b трябва да бъде m (n -> n) или (-> r) (n -> n). След като заместих азбучни променливи, получих грешен резултат a -> Maybe b, а не a -> a. - person Larynx; 29.10.2020
comment
Най-накрая го разбрах! Дължа ти толкова много. Благодари ти. - person Larynx; 31.10.2020