Birisinin foldlM tanımını bu şekilde yazması için ne tür bilgi veya eğitim gereklidir? [kapalı]


9

Son zamanlarda Haskell'i gerçek vaka üretim sistemimin bazılarında kullanmaya çalışıyorum. Haskell tipi sistem gerçekten bana çok yardımcı oluyor. Örneğin, bir tür işleve ihtiyacım olduğunu fark ettiğimde

f :: (Foldable t, Monad m) => ( a-> b -> m b) -> b -> t a -> m b

Aslında foldM, foldlMve gibi işlevler vardır foldrM.

Ancak, gerçekte beni şok eden şey bu işlevlerin tanımıdır, örneğin:

foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldr f' return xs z0
  where f' x k z = f z x >>= k

bu nedenle işlev f'türünde olmalıdır:

f' :: a -> b -> b

gerektiği gibi foldr, o zaman btür olmalıdır *-> m *, böylece tüm tanımı foldlMmantıklı olabilir.

Başka bir örnek liftA2ve<*>

(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 id

liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x = (<*>) (fmap f x)

Kaynak koduna bakmadan önce bazı çözümlerimi denedim. Ama boşluk o kadar büyük ki, gelecekte kaç satır kod yazacağım önemli değil, bu çözümle gelebileceğimi sanmıyorum.

Benim sorum şu ki, birisinin böylesine son derece soyutlanmış bir düzeyde akıl yürütmesi için ne tür bir bilgi ya da hangi matematik dalının gerekli olduğu.

Kategori teorisinin biraz yardımcı olabileceğini biliyorum ve bu harika dersi uzun zamandır takip ediyorum ve hala üzerinde çalışıyorum.


13
Haskell bir dildir. Birçok kelimesi vardır ve bu kelimelerin çoğu çeşitli şekillerde kullanılabilir. Yeni bir dil öğrenirken, yıllarca birçok cümle ve deyim anlamlı değildir. Ama ne kadar çok kullanırsanız, tanıdık kalıpları o kadar çok görürsünüz ve bir zamanlar korkutucu ve gelişmiş olduğunu düşündüğünüz şeyler oldukça doğal gelir. Rahatlayın.
luqui

Yanıtlar:


3

Genel olarak, mantık vb. Ama bunu yaparak da öğrenebilirsiniz. :) Zaman içinde bazı desenler fark, bazı hileler almak.

Bunun gibi foldrekstra bir argümanla. Bazıları onu işlevlere katlanırlar ve böylece birleştirilebilirler .ve id( bazen gerçekten <=<ve return),

foldr g z xs  =  foldr ($) z . map g $ xs
              =  foldr (.) id (map g xs) z
         ~/=  foldr (<=<) return (map g xs) z
{-
  (\f -> f . f) :: (a -> a) -> (a -> a)

  (\f -> f <=< f) :: Monad m => (a -> m a) -> (a -> m a)
                            (still just a type, not a kind)
-}

Bazıları bunu daha basit, sözdizimsel olarak anlamayı daha kolay bulur.

foldr g z [a,b,c,...,n] s =
     g a (foldr g z [b,c,...,n]) s

gikinci argümanında katı olmadığı zaman , sbir örnek olarak, sağda katlanmamıza rağmen, soldan geçen bir durum olarak hizmet edebilir.


1
Çok teşekkür ederim, bu tanımın benzersiz olup olmadığını ve Kleisli kompozisyonunun burada kullanılmasını beklemediğini anlamaya çalışıyordum. Bu cevap şüphemi gerçekten çözüyor.
Theodora

rica ederim. :)
Ness Ness

4

Bunu anlamanın en iyi yolu bunu yapmaktır. Aşağıda bir uygulama var foldlMkullanarak foldlyerine foldr. İyi bir alıştırma, deneyin ve daha sonra önereceğim çözüme gelin. Örnek, bunu başarmak için yaptığım tüm nedenleri açıklıyor, bu sizinkinden farklı olabilir ve önyargı olabilir, çünkü zaten bir fonksiyon akümülatörü kullanmayı biliyordum.

1. Adım : Yazım foldlMaçısındanfoldl

-- this doesn't compile because f returning type is (m b) and not just (b) 
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f z0 xs 

-- So let substitute f by some undefined f'
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
  where f' = undefined

-- cool, but f' should use f somehow in order to get the monadic behaviour
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
  where f' b a = f somethingIDontkNow 

Burada f'bunun saf olduğunu fark edersiniz fve tür eşleşmesinin sonucunu çıkarmanız gerekir . Monadik bir değeri 'çıkarmanın' tek yolu >>=operatöre aittir , ancak böyle bir operatörün kullanıldıktan hemen sonra sarılması gerekir.

Sonuç olarak: Ne zaman bitirirseniz bu monad'ı tamamen açmak istiyorum , sadece pes et. Doğru yol değil

Adım 2 : Hadi yazma için deneyin foldlMaçısından foldlama ilk kullanan []katlanabilir olarak, bu (yani biz aslında kullanımına gerek yoktur desen maç kolaydır çünkü fold)

-- This is not very hard. It is pretty standard recursion schema. :)
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 []     = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs

Tamam, bu kolaydı. foldlTanımı listeler için normal tanımla karşılaştıralım

foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 []     = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs

myfoldl :: (b -> a -> b) -> b -> [a] -> b
myfoldl f z0 []     = z0
myfoldl f z0 (x:xs) = foldl f (f z0 x) xs

Güzel!! hemen hemen aynı. Önemsiz olay da aynı şeyle ilgilidir. Özyinelemeli durum daha böyle bir şey yazmak istiyorum, biraz farklıdır: foldlM' f (f z0 x) xs. Ancak adım 1'deki gibi derlenmez, bu yüzden Tamam, uygulamak istemiyorum f, sadece böyle bir hesaplama yapmak ve onu oluşturmak için>>= düşünebilirsiniz . DahafoldlM' f (f z0 x >>=) xs mantıklıymış gibi bir şey yazmak istiyorum ...

Adım 3 Biriktirmek istediğiniz şeyin bir sonuç değil, bir fonksiyon kompozisyonu olduğunu anlayın. ( burada muhtemelen zaten yayınladığınız için zaten bildiğim gerçeği ile yanlıyorum ).

foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' initFunc xs
  where initFunc = undefined :: b -> m b
        f'       = undefined :: (b -> m b) -> a -> (b -> m b) -- This type signature can be deduce because f' should be applied to initFunc and a's from t a. 

initFuncAdım 2'den (özyinelemeli tanım) elde ettiğimiz bilginin türü ve kullanımı ile bunu çıkarabiliriz initFunc = return. Tanımı f'bilerek tamamlanabilir f'kullanmalıdır fve >>=.

foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' return xs z0
--                        ^^^^^^
--                        |- Initial value
  where f' b a = \bvalue -> b bvalue >>= \bresult -> f bresult a -- this is equivalent to (b >=> \result -> f result a) which captures the sequence behaviour of the implementation
--         ^      ^^^^^^                  ^^^^^^^
--         |      |                       |- This is the result of previous computation
--         |      |- f' should return a function b -> m b. Any time you have to return a function, start writing a lambda  
--         |- This b is the accumulated value and has type b -> m b
-- Following the types you can write this with enough practise

Gördüğünüz gibi, bunu yapmak çok zor değil. Uygulamaya ihtiyacı var, ama profesyonel bir haskell geliştiricisi değilim ve kendim yapabilirim, bu bir pratik meselesi


1
Sol katın anlaşılmasını kolaylaştıran şeyin sağ kattan daha kolay olduğunu gerçekten görmüyorum. Doğru kıvrımın sonsuz yapılar için yararlı bir sonuç üretmesi ve tipik Monadörnekler için verimli olması çok daha olasıdır .
dfeuer

@dfeuer Bunun amacı daha kolay bir örnek göstermek değil, OP için uygun bir alıştırma önermek ve çözümün mantıklı bir argümanını ortaya koymak, süper usta bir haskeller olmanın gerekli olmadığını kanıtlamaya çalışmaktır. böyle bir çözüm. Verimlilik hakkındaki görüşler hesaba katılmaz
lsmor

3

Matematik gibi belirli bir fonksiyon yazmak için herhangi bir bilgiye ihtiyacınız yoktur foldM. Haskell'i zaten 4 yıldır üretimde kullanıyorum ve bu tanımını anlamakta zorlanıyorum foldM. Ancak bunun nedeni çoğunlukla kötü yazılmış olmasıdır. Bazı belirsiz kodları anlayamıyorsanız, lütfen kişisel bir hata olarak almayın. İşte daha okunabilir bir sürümüfoldlM

foldlM
    :: forall t m a b .
       (Foldable t, Monad m)
    => (b -> a -> m b)  -- ^ Monadic action
    -> b                -- ^ Starting accumulator
    -> t a              -- ^ List of values
    -> m b              -- ^ Computation result inside a monad
foldlM f z xs = (foldr step pure xs) z
  where
    step :: a -> (b -> m b) -> b -> m b
    step cur next acc = do
        result <- f acc cur
        next result

Bu işlev hala en kolay işlev değildir. Çoğunlukla foldrara akümülatörün bir fonksiyon olduğu yerlerde tipik olmayan kullanımı olduğu için. Ancak, bu tanımı daha okunaklı hale getiren birkaç mayıs görebilirsiniz:

  1. Fonksiyon argümanları hakkında yorumlar.
  2. Daha iyi argüman isimleri (hala kısa ve deyimsel, ancak en azından daha okunabilir).
  3. İçindeki işlevin açık tip imzası where(böylece bağımsız değişkenlerin şeklini biliyorsunuz).

Böyle bir işlevi gördükten sonra , tanımı adım adım genişletmek ve nasıl çalıştığını görmek için Denklem muhakeme tekniği uygulayabilirsiniz. Bu tür işlevleri yerine getirme yeteneği deneyim ile birlikte gelir. Güçlü matematikçi becerilerim yok ve bu işlev tipik bir Haskell işlevi değil. Ama ne kadar pratik yaparsanız o kadar iyi olur :)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.