dflemstrの答えは的確ですが、2つのコメントを追加したいと思いました(元の答えにコメントで対応することはできません)。
まず、2番目の定義でメモリを節約できるのと同じロジックで、これについても同様の議論を行うことができます。
data Tree a = Empty
| Leaf a
| LeftOnly a (Tree a)
| RightOnly a (Tree a)
| Branch a (Tree a) (Tree a)
これが実際に重要かどうかは、アプリケーションによって異なります。
2番目の、そしてより重要な注意点は、データコンストラクターを直接使用しないようにすると、これらの実装の選択肢から抽象化できるということです。たとえば、foldTree
これらのタイプのいずれに対しても同等の関数を記述できます。短いタイプの場合は、次のようにします。
data Tree a = Empty | Node a (Tree a) (Tree a)
foldTree :: (a -> b -> b -> b) -> b -> Tree a -> b
foldTree f z Empty = z
foldTree f z (Node v l r) = f v (subfold l) (subfold r)
where subfold = foldTree f z
そして長いものについては、次のように書くことができます:
data Tree a = Empty | Leaf a | Node a (Tree a) (Tree a)
foldTree :: (a -> b -> b -> b) -> b -> Tree a -> b
foldTree f z Empty = z
foldTree f z (Leaf v) = f v z z
foldTree f z (Node v l r) = f v (subfold l) (subfold r)
where subfold = foldTree f z
同じことは、あなたのMaybe
ベースの代替案または私の5コンストラクターの代替案に対しても行うことができます。また、この手法は、必要なツリー上の他の一般的な関数に適用できます。(実際、これらの関数の多くは、の観点から記述できるfoldTree
ため、そのほとんどは上記の定義から外れています。)