Согласно этой статье,
Перечисления не учитываются как типы одного конструктора в отношении GHC, поэтому они не извлекаются из распаковки при использовании в качестве строгих полей конструктора или строгих аргументов функции. Это недостаток в GHC, но его можно обойти.
И вместо этого рекомендуется использовать новые типы. Однако я не могу проверить это с помощью следующего кода:
{-# LANGUAGE MagicHash,BangPatterns #-}
{-# OPTIONS_GHC -O2 -funbox-strict-fields -rtsopts -fllvm -optlc --x86-asm-syntax=intel #-}
module Main(main,f,g)
where
import GHC.Base
import Criterion.Main
data D = A | B | C
newtype E = E Int deriving(Eq)
f :: D -> Int#
f z | z `seq` False = 3422#
f z = case z of
A -> 1234#
B -> 5678#
C -> 9012#
g :: E -> Int#
g z | z `seq` False = 7432#
g z = case z of
(E 0) -> 2345#
(E 1) -> 6789#
(E 2) -> 3535#
f' x = I# (f x)
g' x = I# (g x)
main :: IO ()
main = defaultMain [ bench "f" (whnf f' A)
, bench "g" (whnf g' (E 0))
]
Глядя на сборку, теги для каждого конструктора перечисления D фактически распаковываются и непосредственно жестко закодированы в инструкции. Кроме того, функция f
не имеет кода обработки ошибок и более чем на 10% быстрее, чем g
. В более реалистичном случае я также испытал замедление после преобразования перечисления в новый тип. Может ли кто-нибудь дать мне некоторое представление об этом? Спасибо.