Я думаю, что этот вопрос о производительности. Хотя семантически сопоставление с образцом проверяется сверху вниз, большинство компиляторов Haskell оптимизируют сопоставление конструкторов ADT с эквивалентом оператора C switch
.
Вы можете думать, что представление данных для ADT имеет «тег», который говорит, с каким конструктором он был сделан, вместе с указателем для каждого аргумента. Так, например, Nothing
может быть представлено как 0 (null)
, а Just 42
представлено как 1 (pointer to 42)
.
Тогда в такой функции:
squash :: Maybe (Maybe Int) -> Int
squash (Just Nothing) = 0
squash Nothing = 0
squash (Just (Just x)) = x
Компилятор установит дерево решений:
squash x =
check tag of x:
0 -> 0
1 y -> check tag of y:
0 -> 0
1 z -> z
Там, где каждый тег вычисляется с помощью таблицы переходов или чего-то еще, так что проверять 0
как 1
не дороже. Обратите внимание, что это одно и то же дерево решений будет принято независимо от того, каков первоначальный порядок шаблонов в нашем определении.
Тем не менее, при использовании защиты вместо сопоставления в конструкторе, шаблоны, скорее всего, проверяются сверху вниз (для оптимизации этого компилятор должен быть очень умным). Так что, если мы написали fromJust
таким загадочным образом:
fromJust x
| isNothing x = error "fromJust: Nothing"
| isJust x = case x of { Just y -> y }
Тогда это, вероятно, будет проверять каждый конструктор по очереди, и мы могли бы оптимизировать, переключая порядок дел. К счастью, писать так, как это важно, громоздко: -).