1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Haskellで正格評価を使うことで動作が変わる例 unsafeTake

1
Last updated at Posted at 2026-04-05

これは何?

Haskellのtakeの実装を見ていたら、!_という書き方がされていて面白かったので簡単にまとめる。


実装を書き換えつつ、手元で動かしてみる

{-# LANGUAGE BangPatterns #-}
take' :: Int -> [a] -> [a]
take' n xs
  | 0 < n = unsafeTake n xs
  | otherwise = []

-- A version of take that takes the whole list if it's given an argument less
-- than 1.
{-# NOINLINE [0] unsafeTake #-} -- See Note [Inline FB functions]
unsafeTake :: Int -> [a] -> [a]
unsafeTake !_ [] = []
-- unsafeTake _ [] = []
unsafeTake 1 (x : _) = [x]
unsafeTake m (x : xs) = x : unsafeTake (m - 1) xs

main :: IO ()
main = do
  print $ take' 3 [0 ..]

unsafeTakeの中に!_というワイルドカードを正格評価にしている箇所がある。

takeを使用する場合、これをunsafeTake _ [] = []に変更しても動作に影響しない。

しかし、unsafaTakeを直接呼ぶ場合には話が違ってくる。
_を使うとprint (unsafeTake undefined ([] :: [Int]))の結果が[]になるのに対して、
!_を使用するとエラーが発生する。

-- _を使う例
{-# LANGUAGE BangPatterns #-}
-- A version of take that takes the whole list if it's given an argument less
-- than 1.
{-# NOINLINE [0] unsafeTake #-} -- See Note [Inline FB functions]
unsafeTake :: Int -> [a] -> [a]
-- unsafeTake !_ [] = []
unsafeTake _ [] = []
unsafeTake 1 (x : _) = [x]
unsafeTake m (x : xs) = x : unsafeTake (m - 1) xs

main :: IO ()
main = do
  print (unsafeTake undefined ([0..] :: [Int])) -- []
-- !_を使う例
{-# LANGUAGE BangPatterns #-}
-- A version of take that takes the whole list if it's given an argument less
-- than 1.
{-# NOINLINE [0] unsafeTake #-} -- See Note [Inline FB functions]
unsafeTake :: Int -> [a] -> [a]
unsafeTake !_ [] = []
-- unsafeTake _ [] = []
unsafeTake 1 (x : _) = [x]
unsafeTake m (x : xs) = x : unsafeTake (m - 1) xs

main :: IO ()
main = do
  print (unsafeTake undefined ([0..] :: [Int])) -- 実行時エラー
test.hs: Prelude.undefined
CallStack (from HasCallStack):
  undefined, called at test.hs:19:21 in main:Main

なぜ、これが起こるのか

Haskellは遅延評価型の言語であるため、_は評価されずに終了する。そのため、undefinedが投入可能になる。
一方で!_undefinedが評価されるため、実行時エラーが発生する。


では、_に対してなんでも正格評価を使うべきなのか

意味によると思っている。
takeの場合には指定された数だけリストから要素を取り出すのでundefined個取り出すというのは例外として扱うべきだと思う。

逆にconst関数のような、_の値が関係ない関数の場合には遅延評価を使うべきなのかと思った。

-- 2つ引数を受取り、1つ目だけを返す関数
const :: a -> b -> a
const x _ = x

constって何につかうんだろうと思った方がいれば、Haskellのconst関数って何に使うんだろ...。を読むと良い。

つまり、意味的に適切になるのはどちらなのかで使い分けると良さそう。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?