Bu uygulama neden Katlanabilir Tip Sınıfının kötü bir örneğidir?
Harika Haskell Kitabı üzerinde çalışıyorum . Geçilebilir bölümün (21) sonunda, aşağıdaki Ağaç için bir örnek yazmam gerekiyor:
data Tree a =
Empty
| Leaf a
| Node (Tree a) a (Tree a)
İşte çözümümün tam koduna bir bağlantı . Egzersizleri hem uygulamaya çalışıyor önerir foldMap
ve foldr
. Bu şekilde uyguladım foldr
(çağrı sırasına fazla düşünmeden):
foldr _ z Empty = z
foldr f z (Leaf x) = f x z
foldr f z (Node left x right) =
f x $ foldr f (foldr f z left) right
Daha sonra foldMap
aşağıdaki gibi uyguladım :
foldMap f Empty = mempty
foldMap f (Leaf x) = f x
foldMap f (Node left x right) =
foldMap f left <> f x <> foldMap f right
QuickCheck'in foldable
test grubunu çalıştırdığımda bazı hatalar alıyorum. Benim Değişen foldr
tüm testler geçmesi aşağıdaki modellerine uygulanması:
foldr _ z Empty = z
foldr f z (Leaf x) = f x z
foldr f z (Node left x right) =
foldr f (f x (foldr f z right)) left
Başarısız olan test durumunu kendi başıma çalıştırmayı denedim, ancak başarısızlığı yeniden oluşturamadım:
*Ch21_12_ExercisesTree Data.Monoid> tree = Node (Node (Leaf (-5)) 3 (Node (Leaf 3) 5 Empty)) (-2) Empty
*Ch21_12_ExercisesTree Data.Monoid> foldr (<>) (mempty :: Sum Int) t
Sum {getSum = 4}
*Ch21_12_ExercisesTree Data.Monoid> foldMap Sum t
Sum {getSum = 4}
fold
QuickCheck'in kullandığı ing işlevi hakkında anlamadığım bir şey olduğundan şüpheleniyorum .
Sorular:
- Başarısızlıklar neden oluyor?
- QuickCheck tarafından testte kullanılan işlevi almanın bir yolu var mı?
Yanıtlar
foldr
değerleri (monoid olarak) oluşturulabilen fonksiyonlara çeviren fonksiyon ile monoid foldMap
kullanılarakEndo elde edilebilir. Öyleyse, eğer senin ...a -> b -> b
a
b -> b
foldMap
foldMap f Empty = mempty
foldMap f (Leaf x) = f x
foldMap f (Node left x right) =
foldMap f left <> f x <> foldMap f right
... karşılık gelen şu foldr
olmalıdır:
foldr f z Empty = id z -- mempty amounts to id
foldr f z (Leaf x) = (f x) z
foldr f z (Node left x right) =
((\e -> foldr f e left) . f x . (\e -> foldr f e right)) z -- (<>) amounts to (.)
Biraz düzeltirsek ...
foldr f z Empty = z
foldr f z (Leaf x) = f x z
foldr f z (Node left x right) =
foldr f (f x (foldr f z right)) left)
... foldr
sorunuzda yazıldığı şekliyle doğru tanımını alıyoruz . Gerçekleştirmeler arasındaki farkın kompozisyon sırasına bağlı olması gerektiğinden, sizin de öğrendiğiniz gibi , değişmeyen bir monoidin denenmesi kolaylıkla başarısız bir duruma yol açar .
QuickCheck alt sorusunda, DDub'ın cevabını kabul ediyorum . .
Daha önce çıkardığınız gibi, başarısızlık almanızın nedeni, iki uygulamanın ayırt edilebilir olmasıdır, bu, değişmeli olmayan bir monoid kullanarak gözlemleyebilirsiniz.
Quickcheck tarafından kullanılan işlevi almak o kadar basit değil. Örneğin, biraz daha fazla bilgi için hızlı kontrol tarafından oluşturulan işlevlerle ilgili bu soruya / cevaba bakın Show
.
Almanın yolu Show
QuickCheck dışına mümkün fonksiyonlarını işlevi sarılmasıdır türü . Bununla birlikte, aradığınız kod ( burada bulunur ) yalnızca işlevleri doğrudan kullanır, böylece bunlar asla gösterilemezler. Deneyebileceğiniz bir seçenek, işlevi yerine ve gerektiği gibi uygulayacağınız işlevin kendi sürümünüzü oluşturmaktır .Funfoldable
Fun a b
a -> b
applyFun
Değişmeli bir Monoid kullandığımı yeni fark ettim ... Değişmeli olmayan bir Monoid kullanarak başarısızlığı yeniden yaratabildim:
> ftree = fmap (First . Just) tree
> foldr (<>) mempty ft
First {getFirst = Just (-2)}
> foldMap (First . Just) ft
First {getFirst = Just (First {getFirst = Just (-5)})}
Bu muhtemelen basit bir durumdur. Gerçek veri türlerine sahip üretim kodunda bunun çok daha karmaşık olabileceğini hayal ediyorum.