9 표준 프렐류드

9 표준 프렐류드 (Standard Prelude)

하스켈로 프로그램을 짜기 시작하면 가장 먼저 만나는 것이 바로 프렐류드예요. import 한 줄 없이 바로 쓸 수 있는 모든 함수와 타입, 타입클래스가 여기 모여 있죠. 그런데 이번 장은 프렐류드를 설명하는 장이 아니라, 프렐류드의 명세 그 자체를 통째로 담은 문서예요. 즉 평소에 자연스럽게 써 온 map, foldr, (++) 같은 함수들이 실제로 어떻게 정의되어 있는지를 한눈에 볼 수 있는 곳이랍니다. 코드를 천천히 읽다 보면 「아, 이게 그 의미였구나」 하는 순간이 꽤 많아질 거예요. 특히 이 정의들이 효율보다 명확성을 우선해서 쓰여 있다는 점만 기억해 두면 독해가 훨씬 수월해져요.

출처: https://www.haskell.org/onlinereport/haskell2010/haskellch9.html

본문

이 장에서는 하스켈 프렐류드(Prelude) 전체를 싣고 있어요. 그리고 이 코드 자체가 곧 프렐류드에 대한 명세(specification)가 됩니다. 정의 중 상당수가 효율보다는 명확성에 초점을 맞춰 작성됐기 때문에, 「이대로 구현해야 한다」고 생각할 필요는 없어요. 각 함수가 어떤 의미로 동작한다는 기준점을 보여 주는 문서라고 보면 돼요.

클래스 선언에 함께 따라오는 기본 메서드(default method) 정의는 말 그대로 기본값에 대한 명세일 뿐이에요. 모든 인스턴스에서 그 메서드의 의미를 구속하는 건 아니죠. 좋은 예가 하나 있어요. Enum 클래스의 enumFrom 기본 메서드는, 타입의 값 범위가 Int를 넘어가는 경우 제대로 동작하지 않아요. fromEnum이 그 타입의 모든 값을 서로 다른 Int 값으로 대응시킬 수 없기 때문이에요.

여기 실린 프렐류드는 루트 모듈인 Prelude와, 그 아래 세 개의 하위 모듈 PreludeList, PreludeText, PreludeIO로 나뉘어 있어요. 다만 이 구조는 순전히 보여 주기 위한 것뿐이에요. 구현체가 프렐류드를 꼭 이런 식으로 구성해야 하는 것도 아니고, 이 세 모듈을 따로 import 할 수 있는 것도 아니에요. 실제로 의미가 있는 건 오직 module Prelude가 내보내는 것(export)뿐이에요.

이 모듈 중 일부는 Data.Char, Control.Monad, System.IO, Numeric 같은 라이브러리 모듈을 import 해요. 이 모듈들은 이 보고서의 Part II에서 자세히 다루는데, 그런 import는 당연히 프렐류드 명세의 일부가 아니에요. 구현체가 라이브러리 모듈을 얼마든지 더 가져오거나 덜 가져와도 된다는 뜻이에요.

하스켈로는 정의할 수 없는 프리미티브, 즉 이름이 prim으로 시작하는 것들은 시스템에 의존적인 방식으로 module PreludeBuiltin에 정의돼 있고, 여기서는 보여 주지 않아요. 프리미티브를 클래스 메서드에 그냥 묶어 놓은 인스턴스 선언도 생략했고, 너무 장황하면서 동작이 뻔히 보이는 인스턴스 몇 개도 간결함을 위해 빼뒀어요.

Integer() 같은 특별한 타입의 선언도 완결성을 위해 프렐류드에 넣었지만, 그 선언 자체는 불완전하거나 문법적으로 유효하지 않을 수 있어요. 하스켈로는 표현할 수 없는 나머지 정의가 들어갈 자리에는 생략 부호 ...가 자주 쓰여요.

뜻밖의 모호성(ambiguity) 오류를 줄이고 효율을 높이기 위해, 자주 쓰이는 리스트 함수 몇몇은 Integral aNum a 같은 일반적인 숫자 타입 대신 Int 타입을 사용해요. 그 함수들은 take, drop, !!, length, splitAt, replicate이에요. 더 일반화된 버전은 Data.List 라이브러리에 generic이라는 접두어를 붙여서 있어요. 예를 들어 genericLength처럼요.

module Prelude (
 
    module PreludeList, module PreludeText, module PreludeIO,
 
    Bool(False, True),
 
    Maybe(Nothing, Just),
 
    Either(Left, Right),
 
    Ordering(LT, EQ, GT),
 
    Char, String, Int, Integer, Float, Double, Rational, IO,


--      These built-in types are defined in the Prelude, but
 
--      are denoted by built-in syntax, and cannot legally
 
--      appear in an export list.
 
--  List type: []((:), [])
 
--  Tuple types: (,)((,)), (,,)((,,)), etc.
 
--  Trivial type: ()(())
 
--  Functions: (->)
 

 
    Eq((==), (/=)),
 
    Ord(compare, (<), (<=), (>=), (>), max, min),
 
    Enum(succ, pred, toEnum, fromEnum, enumFrom, enumFromThen,
 
         enumFromTo, enumFromThenTo),
 
    Bounded(minBound, maxBound),
 
    Num((+), (-), (⋆), negate, abs, signum, fromInteger),
 
    Real(toRational),
 
    Integral(quot, rem, div, mod, quotRem, divMod, toInteger),
 
    Fractional((/), recip, fromRational),
 
    Floating(pi, exp, log, sqrt, (⋆⋆), logBase, sin, cos, tan,
 
             asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh),
 
    RealFrac(properFraction, truncate, round, ceiling, floor),
 
    RealFloat(floatRadix, floatDigits, floatRange, decodeFloat,
 
              encodeFloat, exponent, significand, scaleFloat, isNaN,
 
              isInfinite, isDenormalized, isIEEE, isNegativeZero, atan2),
 
    Monad((>>=), (>>), return, fail),
 
    Functor(fmap),
 
    mapM, mapM_, sequence, sequence_, (=<<),
 
    maybe, either,
 
    (&&), (||), not, otherwise,
 
    subtract, even, odd, gcd, lcm, (^), (^^),
 
    fromIntegral, realToFrac,
 
    fst, snd, curry, uncurry, id, const, (.), flip, ($), until,
 
    asTypeOf, error, undefined,
 
    seq, ($!)
 
  ) where


import PreludeBuiltin                      -- Contains all ‘prim' values
 
import UnicodePrims( primUnicodeMaxChar )  -- Unicode primitives
 
import PreludeList
 
import PreludeText
 
import PreludeIO
 
import Data.Ratio( Rational )


infixr 9  .
 
infixr 8  ^, ^^, ⋆⋆
 
infixl 7  ⋆, /, ‘quot‘, ‘rem‘, ‘div‘, ‘mod‘
 
infixl 6  +, -


-- The (:) operator is built-in syntax, and cannot legally be given
 
-- a fixity declaration; but its fixity is given by:
 
--   infixr 5  :
 

 
infix  4  ==, /=, <, <=, >=, >
 
infixr 3  &&
 
infixr 2  ||
 
infixl 1  >>, >>=
 
infixr 1  =<<
 
infixr 0  $, $!, ‘seq‘


-- Standard types, classes, instances and related functions
 

 
-- Equality and Ordered classes
 

 
class  Eq a  where
 
    (==), (/=) :: a -> a -> Bool
 

 
        -- Minimal complete definition:
 
        --      (==) or (/=)
 
    x /= y     =  not (x == y)
 
    x == y     =  not (x /= y)


class  (Eq a) => Ord a  where
 
    compare              :: a -> a -> Ordering
 
    (<), (<=), (>=), (>) :: a -> a -> Bool
 
    max, min             :: a -> a -> a
 

 
        -- Minimal complete definition:
 
        --      (<=) or compare
 
        -- Using compare can be more efficient for complex types.
 
    compare x y
 
         | x == y    =  EQ
 
         | x <= y    =  LT
 
         | otherwise =  GT
 

 
    x <= y           =  compare x y /= GT
 
    x <  y           =  compare x y == LT
 
    x >= y           =  compare x y /= LT
 
    x >  y           =  compare x y == GT


-- note that (min x y, max x y) = (x,y) or (y,x)
 
    max x y
 
         | x <= y    =  y
 
         | otherwise =  x
 
    min x y
 
         | x <= y    =  x
 
         | otherwise =  y


-- Enumeration and Bounded classes
 

 
class  Enum a  where
 
    succ, pred       :: a -> a
 
    toEnum           :: Int -> a
 
    fromEnum         :: a -> Int
 
    enumFrom         :: a -> [a]             -- [n..]
 
    enumFromThen     :: a -> a -> [a]        -- [n,n'..]
 
    enumFromTo       :: a -> a -> [a]        -- [n..m]
 
    enumFromThenTo   :: a -> a -> a -> [a]   -- [n,n'..m]
 

 
        -- Minimal complete definition:
 
        --      toEnum, fromEnum
 
        --
 
        -- NOTE: these default methods only make sense for types
 
        --       that map injectively into Int using fromEnum
 
        --       and toEnum.
 
    succ             =  toEnum . (+1) . fromEnum
 
    pred             =  toEnum . (subtract 1) . fromEnum
 
    enumFrom x       =  map toEnum [fromEnum x ..]
 
    enumFromTo x y   =  map toEnum [fromEnum x .. fromEnum y]
 
    enumFromThen x y =  map toEnum [fromEnum x, fromEnum y ..]
 
    enumFromThenTo x y z =
 
                        map toEnum [fromEnum x, fromEnum y .. fromEnum z]


class  Bounded a  where
 
    minBound         :: a
 
    maxBound         :: a


-- Numeric classes
 

 
class  (Eq a, Show a) => Num a  where
 
    (+), (-), (⋆)    :: a -> a -> a
 
    negate           :: a -> a
 
    abs, signum      :: a -> a
 
    fromInteger      :: Integer -> a
 

 
        -- Minimal complete definition:
 
        --      All, except negate or (-)
 
    x - y            =  x + negate y
 
    negate x         =  0 - x


class  (Num a, Ord a) => Real a  where
 
    toRational       ::  a -> Rational


class  (Real a, Enum a) => Integral a  where
 
    quot, rem        :: a -> a -> a
 
    div, mod         :: a -> a -> a
 
    quotRem, divMod  :: a -> a -> (a,a)
 
    toInteger        :: a -> Integer
 

 
        -- Minimal complete definition:
 
        --      quotRem, toInteger
 
    n ‘quot‘ d       =  q  where (q,r) = quotRem n d
 
    n ‘rem‘ d        =  r  where (q,r) = quotRem n d
 
    n ‘div‘ d        =  q  where (q,r) = divMod n d
 
    n ‘mod‘ d        =  r  where (q,r) = divMod n d
 
    divMod n d       =  if signum r == - signum d then (q-1, r+d) else qr
 
                        where qr@(q,r) = quotRem n d


class  (Num a) => Fractional a  where
 
    (/)              :: a -> a -> a
 
    recip            :: a -> a
 
    fromRational     :: Rational -> a
 

 
        -- Minimal complete definition:
 
        --      fromRational and (recip or (/))
 
    recip x          =  1 / x
 
    x / y            =  x ⋆ recip y


class  (Fractional a) => Floating a  where
 
    pi                  :: a
 
    exp, log, sqrt      :: a -> a
 
    (⋆⋆), logBase       :: a -> a -> a
 
    sin, cos, tan       :: a -> a
 
    asin, acos, atan    :: a -> a
 
    sinh, cosh, tanh    :: a -> a
 
    asinh, acosh, atanh :: a -> a
 

 
        -- Minimal complete definition:
 
        --      pi, exp, log, sin, cos, sinh, cosh
 
        --      asin, acos, atan
 
        --      asinh, acosh, atanh
 
    x ⋆⋆ y           =  exp (log x ⋆ y)
 
    logBase x y      =  log y / log x
 
    sqrt x           =  x ⋆⋆ 0.5
 
    tan  x           =  sin  x / cos  x
 
    tanh x           =  sinh x / cosh x


class  (Real a, Fractional a) => RealFrac a  where
 
    properFraction   :: (Integral b) => a -> (b,a)
 
    truncate, round  :: (Integral b) => a -> b
 
    ceiling, floor   :: (Integral b) => a -> b
 

 
        -- Minimal complete definition:
 
        --      properFraction
 
    truncate x       =  m  where (m,_) = properFraction x
 

 
    round x          =  let (n,r) = properFraction x
 
                            m     = if r < 0 then n - 1 else n + 1
 
                          in case signum (abs r - 0.5) of
 
                                -1 -> n
 
                                0  -> if even n then n else m
 
                                1  -> m
 

 
    ceiling x        =  if r > 0 then n + 1 else n
 
                        where (n,r) = properFraction x
 

 
    floor x          =  if r < 0 then n - 1 else n
 
                        where (n,r) = properFraction x


class  (RealFrac a, Floating a) => RealFloat a  where
 
    floatRadix       :: a -> Integer
 
    floatDigits      :: a -> Int
 
    floatRange       :: a -> (Int,Int)
 
    decodeFloat      :: a -> (Integer,Int)
 
    encodeFloat      :: Integer -> Int -> a
 
    exponent         :: a -> Int
 
    significand      :: a -> a
 
    scaleFloat       :: Int -> a -> a
 
    isNaN, isInfinite, isDenormalized, isNegativeZero, isIEEE
 
                     :: a -> Bool
 
    atan2            :: a -> a -> a
 

 
        -- Minimal complete definition:
 
        --      All except exponent, significand,
 
        --                 scaleFloat, atan2
 
    exponent x       =  if m == 0 then 0 else n + floatDigits x
 
                        where (m,n) = decodeFloat x
 

 
    significand x    =  encodeFloat m (- floatDigits x)
 
                        where (m,_) = decodeFloat x
 

 
    scaleFloat k x   =  encodeFloat m (n+k)
 
                        where (m,n) = decodeFloat x
 

 
    atan2 y x
 
      | x>0           =  atan (y/x)
 
      | x==0 && y>0   =  pi/2
 
      | x<0  && y>0   =  pi + atan (y/x)
 
      |(x<=0 && y<0)  ||
 
       (x<0 && isNegativeZero y) ||
 
       (isNegativeZero x && isNegativeZero y)
 
                      = -atan2 (-y) x
 
      | y==0 && (x<0 || isNegativeZero x)
 
                      =  pi    -- must be after the previous test on zero y
 
      | x==0 && y==0  =  y     -- must be after the other double zero tests
 
      | otherwise     =  x + y -- x or y is a NaN, return a NaN (via +)


-- Numeric functions
 

 
subtract         :: (Num a) => a -> a -> a
 
subtract         =  flip (-)


even, odd        :: (Integral a) => a -> Bool
 
even n           =  n ‘rem‘ 2 == 0
 
odd              =  not . even


gcd              :: (Integral a) => a -> a -> a
 
gcd 0 0          =  error "Prelude.gcd: gcd 0 0 is undefined"
 
gcd x y          =  gcd' (abs x) (abs y)
 
                    where gcd' x 0  =  x
 
                          gcd' x y  =  gcd' y (x ‘rem‘ y)


lcm              :: (Integral a) => a -> a -> a
 
lcm _ 0          =  0
 
lcm 0 _          =  0
 
lcm x y          =  abs ((x ‘quot‘ (gcd x y)) ⋆ y)


(^)              :: (Num a, Integral b) => a -> b -> a
 
x ^ 0            =  1
 
x ^ n | n > 0    =  f x (n-1) x
 
                    where f _ 0 y = y
 
                          f x n y = g x n  where
 
                                    g x n | even n  = g (x⋆x) (n ‘quot‘ 2)
 
                                          | otherwise = f x (n-1) (x⋆y)
 
_ ^ _            = error "Prelude.^: negative exponent"


(^^)             :: (Fractional a, Integral b) => a -> b -> a
 
x ^^ n           =  if n >= 0 then x^n else recip (x^(-n))


fromIntegral     :: (Integral a, Num b) => a -> b
 
fromIntegral     =  fromInteger . toInteger


realToFrac     :: (Real a, Fractional b) => a -> b
 
realToFrac      =  fromRational . toRational


-- Monadic classes
 

 
class  Functor f  where
 
    fmap              :: (a -> b) -> f a -> f b


class  Monad m  where
 
    (>>=)  :: m a -> (a -> m b) -> m b
 
    (>>)   :: m a -> m b -> m b
 
    return :: a -> m a
 
    fail   :: String -> m a
 

 
        -- Minimal complete definition:
 
        --      (>>=), return
 
    m >> k  =  m >>= \_ -> k
 
    fail s  = error s


sequence       :: Monad m => [m a] -> m [a]
 
sequence       =  foldr mcons (return [])
 
                    where mcons p q = p >>= \x -> q >>= \y -> return (x:y)


sequence_      :: Monad m => [m a] -> m ()
 
sequence_      =  foldr (>>) (return ())


-- The xxxM functions take list arguments, but lift the function or
 
-- list element to a monad type
 
mapM             :: Monad m => (a -> m b) -> [a] -> m [b]
 
mapM f as        =  sequence (map f as)


mapM_            :: Monad m => (a -> m b) -> [a] -> m ()
 
mapM_ f as       =  sequence_ (map f as)


(=<<)            :: Monad m => (a -> m b) -> m a -> m b
 
f =<< x          =  x >>= f


-- Trivial type
 

 
data  ()  =  ()  deriving (Eq, Ord, Enum, Bounded)
 
        -- Not legal Haskell; for illustration only


-- Function type
 

 
-- identity function
 
id               :: a -> a
 
id x             =  x


-- constant function
 
const            :: a -> b -> a
 
const x _        =  x


-- function composition
 
(.)              :: (b -> c) -> (a -> b) -> a -> c
 
f . g            =  \ x -> f (g x)


-- flip f  takes its (first) two arguments in the reverse order of f.
 
flip             :: (a -> b -> c) -> b -> a -> c
 
flip f x y       =  f y x


seq :: a -> b -> b
 
seq = ...       -- Primitive


-- right-associating infix application operators
 
-- (useful in continuation-passing style)
 
($), ($!) :: (a -> b) -> a -> b
 
f $  x    =  f x
 
f $! x    =  x ‘seq‘ f x


-- Boolean type
 

 
data  Bool  =  False | True     deriving (Eq, Ord, Enum, Read, Show, Bounded)


-- Boolean functions
 

 
(&&), (||)       :: Bool -> Bool -> Bool
 
True  && x       =  x
 
False && _       =  False
 
True  || _       =  True
 
False || x       =  x


not              :: Bool -> Bool
 
not True         =  False
 
not False        =  True


otherwise        :: Bool
 
otherwise        =  True


-- Character type
 

 
data Char = ... 'a' | 'b' ... -- Unicode values


instance  Eq Char  where
 
    c == c'          =  fromEnum c == fromEnum c'


instance  Ord Char  where
 
    c <= c'          =  fromEnum c <= fromEnum c'


instance  Enum Char  where
 
    toEnum            = primIntToChar
 
    fromEnum          = primCharToInt
 
    enumFrom c        = map toEnum [fromEnum c .. fromEnum (maxBound::Char)]
 
    enumFromThen c c' = map toEnum [fromEnum c, fromEnum c' .. fromEnum lastChar]
 
                      where lastChar :: Char
 
                            lastChar | c' < c    = minBound
 
                                     | otherwise = maxBound


instance  Bounded Char  where
 
    minBound  =  '\0'
 
    maxBound  =  primUnicodeMaxChar


type  String = [Char]


-- Maybe type
 

 
data  Maybe a  =  Nothing | Just a      deriving (Eq, Ord, Read, Show)


maybe              :: b -> (a -> b) -> Maybe a -> b
 
maybe n f Nothing  =  n
 
maybe n f (Just x) =  f x


instance  Functor Maybe  where
 
    fmap f Nothing    =  Nothing
 
    fmap f (Just x)   =  Just (f x)


instance  Monad Maybe  where
 
    (Just x) >>= k   =  k x
 
    Nothing  >>= k   =  Nothing
 
    return           =  Just
 
    fail s           =  Nothing


-- Either type
 

 
data  Either a b  =  Left a | Right b   deriving (Eq, Ord, Read, Show)


either               :: (a -> c) -> (b -> c) -> Either a b -> c
 
either f g (Left x)  =  f x
 
either f g (Right y) =  g y


-- IO type
 

 
data IO a = ...         -- abstract


instance  Functor IO where
 
   fmap f x           =  x >>= (return . f)


instance Monad IO where
 
   (>>=)  = ...
 
   return = ...
 
   fail s = ioError (userError s)


-- Ordering type
 

 
data  Ordering  =  LT | EQ | GT
 
          deriving (Eq, Ord, Enum, Read, Show, Bounded)


-- Standard numeric types.  The data declarations for these types cannot
 
-- be expressed directly in Haskell since the constructor lists would be
 
-- far too large.
 

 
data  Int  =  minBound ... -1 | 0 | 1 ... maxBound
 
instance  Eq       Int  where ...
 
instance  Ord      Int  where ...
 
instance  Num      Int  where ...
 
instance  Real     Int  where ...
 
instance  Integral Int  where ...
 
instance  Enum     Int  where ...
 
instance  Bounded  Int  where ...


data  Integer  =  ... -1 | 0 | 1 ...
 
instance  Eq       Integer  where ...
 
instance  Ord      Integer  where ...
 
instance  Num      Integer  where ...
 
instance  Real     Integer  where ...
 
instance  Integral Integer  where ...
 
instance  Enum     Integer  where ...


data  Float
 
instance  Eq         Float  where ...
 
instance  Ord        Float  where ...
 
instance  Num        Float  where ...
 
instance  Real       Float  where ...
 
instance  Fractional Float  where ...
 
instance  Floating   Float  where ...
 
instance  RealFrac   Float  where ...
 
instance  RealFloat  Float  where ...


data  Double
 
instance  Eq         Double  where ...
 
instance  Ord        Double  where ...
 
instance  Num        Double  where ...
 
instance  Real       Double  where ...
 
instance  Fractional Double  where ...
 
instance  Floating   Double  where ...
 
instance  RealFrac   Double  where ...
 
instance  RealFloat  Double  where ...


-- The Enum instances for Floats and Doubles are slightly unusual.
 
-- The ‘toEnum' function truncates numbers to Int.  The definitions
 
-- of enumFrom and enumFromThen allow floats to be used in arithmetic
 
-- series: [0,0.1 .. 0.95].  However, roundoff errors make these somewhat
 
-- dubious.  This example may have either 10 or 11 elements, depending on
 
-- how 0.1 is represented.
 

 
instance  Enum Float  where
 
    succ x           =  x+1
 
    pred x           =  x-1
 
    toEnum           =  fromIntegral
 
    fromEnum         =  fromInteger . truncate   -- may overflow
 
    enumFrom         =  numericEnumFrom
 
    enumFromThen     =  numericEnumFromThen
 
    enumFromTo       =  numericEnumFromTo
 
    enumFromThenTo   =  numericEnumFromThenTo


instance  Enum Double  where
 
    succ x           =  x+1
 
    pred x           =  x-1
 
    toEnum           =  fromIntegral
 
    fromEnum         =  fromInteger . truncate   -- may overflow
 
    enumFrom         =  numericEnumFrom
 
    enumFromThen     =  numericEnumFromThen
 
    enumFromTo       =  numericEnumFromTo
 
    enumFromThenTo   =  numericEnumFromThenTo


numericEnumFrom         :: (Fractional a) => a -> [a]
 
numericEnumFromThen     :: (Fractional a) => a -> a -> [a]
 
numericEnumFromTo       :: (Fractional a, Ord a) => a -> a -> [a]
 
numericEnumFromThenTo   :: (Fractional a, Ord a) => a -> a -> a -> [a]
 
numericEnumFrom         =  iterate (+1)
 
numericEnumFromThen n m =  iterate (+(m-n)) n
 
numericEnumFromTo n m   =  takeWhile (<= m+1/2) (numericEnumFrom n)
 
numericEnumFromThenTo n n' m = takeWhile p (numericEnumFromThen n n')
 
                             where
 
                               p | n' >= n   = (<= m + (n'-n)/2)
 
                                 | otherwise = (>= m + (n'-n)/2)


-- Lists
 

 
data  [a]  =  [] | a : [a]  deriving (Eq, Ord)
 
        -- Not legal Haskell; for illustration only


instance Functor [] where
 
    fmap = map


instance  Monad []  where
 
    m >>= k          = concat (map k m)
 
    return x         = [x]
 
    fail s           = []


-- Tuples
 

 
data  (a,b)   =  (a,b)    deriving (Eq, Ord, Bounded)
 
data  (a,b,c) =  (a,b,c)  deriving (Eq, Ord, Bounded)
 
        -- Not legal Haskell; for illustration only


-- component projections for pairs:
 
-- (NB: not provided for triples, quadruples, etc.)
 
fst              :: (a,b) -> a
 
fst (x,y)        =  x


snd              :: (a,b) -> b
 
snd (x,y)        =  y


-- curry converts an uncurried function to a curried function;
 
-- uncurry converts a curried function to a function on pairs.
 
curry            :: ((a, b) -> c) -> a -> b -> c
 
curry f x y      =  f (x, y)


uncurry          :: (a -> b -> c) -> ((a, b) -> c)
 
uncurry f p      =  f (fst p) (snd p)


-- Misc functions
 

 
-- until p f  yields the result of applying f until p holds.
 
until            :: (a -> Bool) -> (a -> a) -> a -> a
 
until p f x
 
     | p x       =  x
 
     | otherwise =  until p f (f x)


-- asTypeOf is a type-restricted version of const.  It is usually used
 
-- as an infix operator, and its typing forces its first argument
 
-- (which is usually overloaded) to have the same type as the second.
 
asTypeOf         :: a -> a -> a
 
asTypeOf         =  const


-- error stops execution and displays an error message
 

 
error            :: String -> a
 
error            =  primError


-- It is expected that compilers will recognize this and insert error
 
-- messages that are more appropriate to the context in which undefined
 
-- appears.
 

 
undefined        :: a
 
undefined        =  error "Prelude.undefined"

9.1 Prelude — PreludeList (리스트 모듈)

이 모듈은 리스트를 다루는 표준 함수들을 모아 둔 곳이에요. 여기 있는 map, filter, foldl, zip 같은 함수는 이미 자주 써 왔을 것들이죠. Prelude가 이 모듈을 다시 내보내기 때문에 이 함수들을 따로 import 할 필요가 없다는 점이 포인트예요.

-- Standard list functions
 

 
module PreludeList (
 
    map, (++), filter, concat, concatMap,
 
    head, last, tail, init, null, length, (!!),
 
    foldl, foldl1, scanl, scanl1, foldr, foldr1, scanr, scanr1,
 
    iterate, repeat, replicate, cycle,
 
    take, drop, splitAt, takeWhile, dropWhile, span, break,
 
    lines, words, unlines, unwords, reverse, and, or,
 
    any, all, elem, notElem, lookup,
 
    sum, product, maximum, minimum,
 
    zip, zip3, zipWith, zipWith3, unzip, unzip3)
 
  where


import qualified Data.Char(isSpace)


infixl 9  !!
 
infixr 5  ++
 
infix  4  ‘elem‘, ‘notElem‘


-- Map and append
 
map :: (a -> b) -> [a] -> [b]
 
map f []     = []
 
map f (x:xs) = f x : map f xs


(++) :: [a] -> [a] -> [a]
 
[]     ++ ys = ys
 
(x:xs) ++ ys = x : (xs ++ ys)


filter :: (a -> Bool) -> [a] -> [a]
 
filter p []                 = []
 
filter p (x:xs) | p x       = x : filter p xs
 
                | otherwise = filter p xs


concat :: [[a]] -> [a]
 
concat xss = foldr (++) [] xss


concatMap :: (a -> [b]) -> [a] -> [b]
 
concatMap f = concat . map f


-- head and tail extract the first element and remaining elements,
 
-- respectively, of a list, which must be non-empty.  last and init
 
-- are the dual functions working from the end of a finite list,
 
-- rather than the beginning.
 

 
head             :: [a] -> a
 
head (x:_)       =  x
 
head []          =  error "Prelude.head: empty list"


tail             :: [a] -> [a]
 
tail (_:xs)      =  xs
 
tail []          =  error "Prelude.tail: empty list"


last             :: [a] -> a
 
last [x]         =  x
 
last (_:xs)      =  last xs
 
last []          =  error "Prelude.last: empty list"


init             :: [a] -> [a]
 
init [x]         =  []
 
init (x:xs)      =  x : init xs
 
init []          =  error "Prelude.init: empty list"


null             :: [a] -> Bool
 
null []          =  True
 
null (_:_)       =  False


-- length returns the length of a finite list as an Int.
 
length           :: [a] -> Int
 
length []        =  0
 
length (_:l)     =  1 + length l


-- List index (subscript) operator, 0-origin
 
(!!)                :: [a] -> Int -> a
 
xs     !! n | n < 0 =  error "Prelude.!!: negative index"
 
[]     !! _         =  error "Prelude.!!: index too large"
 
(x:_)  !! 0         =  x
 
(_:xs) !! n         =  xs !! (n-1)


-- foldl, applied to a binary operator, a starting value (typically the
 
-- left-identity of the operator), and a list, reduces the list using
 
-- the binary operator, from left to right:
 
--  foldl f z [x1, x2, ..., xn] == (...((z ‘f‘ x1) ‘f‘ x2) ‘f‘...) ‘f‘ xn
 
-- foldl1 is a variant that has no starting value argument, and  thus must
 
-- be applied to non-empty lists.  scanl is similar to foldl, but returns
 
-- a list of successive reduced values from the left:
 
--      scanl f z [x1, x2, ...] == [z, z ‘f‘ x1, (z ‘f‘ x1) ‘f‘ x2, ...]
 
-- Note that  last (scanl f z xs) == foldl f z xs.
 
-- scanl1 is similar, again without the starting element:
 
--      scanl1 f [x1, x2, ...] == [x1, x1 ‘f‘ x2, ...]
 

 
foldl            :: (a -> b -> a) -> a -> [b] -> a
 
foldl f z []     =  z
 
foldl f z (x:xs) =  foldl f (f z x) xs


foldl1           :: (a -> a -> a) -> [a] -> a
 
foldl1 f (x:xs)  =  foldl f x xs
 
foldl1 _ []      =  error "Prelude.foldl1: empty list"


scanl            :: (a -> b -> a) -> a -> [b] -> [a]
 
scanl f q xs     =  q : (case xs of
 
                            []   -> []
 
                            x:xs -> scanl f (f q x) xs)


scanl1           :: (a -> a -> a) -> [a] -> [a]
 
scanl1 f (x:xs)  =  scanl f x xs
 
scanl1 _ []      =  []


-- foldr, foldr1, scanr, and scanr1 are the right-to-left duals of the
 
-- above functions.
 

 
foldr            :: (a -> b -> b) -> b -> [a] -> b
 
foldr f z []     =  z
 
foldr f z (x:xs) =  f x (foldr f z xs)


foldr1           :: (a -> a -> a) -> [a] -> a
 
foldr1 f [x]     =  x
 
foldr1 f (x:xs)  =  f x (foldr1 f xs)
 
foldr1 _ []      =  error "Prelude.foldr1: empty list"


scanr             :: (a -> b -> b) -> b -> [a] -> [b]
 
scanr f q0 []     =  [q0]
 
scanr f q0 (x:xs) =  f x q : qs
 
                     where qs@(q:_) = scanr f q0 xs 


scanr1          :: (a -> a -> a) -> [a] -> [a]
 
scanr1 f []     =  []
 
scanr1 f [x]    =  [x]
 
scanr1 f (x:xs) =  f x q : qs
 
                   where qs@(q:_) = scanr1 f xs 


-- iterate f x returns an infinite list of repeated applications of f to x:
 
-- iterate f x == [x, f x, f (f x), ...]
 
iterate          :: (a -> a) -> a -> [a]
 
iterate f x      =  x : iterate f (f x)


-- repeat x is an infinite list, with x the value of every element.
 
repeat           :: a -> [a]
 
repeat x         =  xs where xs = x:xs


-- replicate n x is a list of length n with x the value of every element
 
replicate        :: Int -> a -> [a]
 
replicate n x    =  take n (repeat x)


-- cycle ties a finite list into a circular one, or equivalently,
 
-- the infinite repetition of the original list.  It is the identity
 
-- on infinite lists.
 

 
cycle            :: [a] -> [a]
 
cycle []         =  error "Prelude.cycle: empty list"
 
cycle xs         =  xs' where xs' = xs ++ xs'


-- take n, applied to a list xs, returns the prefix of xs of length n,
 
-- or xs itself if n > length xs.  drop n xs returns the suffix of xs
 
-- after the first n elements, or [] if n > length xs.  splitAt n xs
 
-- is equivalent to (take n xs, drop n xs).
 

 
take                   :: Int -> [a] -> [a]
 
take n _      | n <= 0 =  []
 
take _ []              =  []
 
take n (x:xs)          =  x : take (n-1) xs


drop                   :: Int -> [a] -> [a]
 
drop n xs     | n <= 0 =  xs
 
drop _ []              =  []
 
drop n (_:xs)          =  drop (n-1) xs


splitAt                  :: Int -> [a] -> ([a],[a])
 
splitAt n xs             =  (take n xs, drop n xs)


-- takeWhile, applied to a predicate p and a list xs, returns the longest
 
-- prefix (possibly empty) of xs of elements that satisfy p.  dropWhile p xs
 
-- returns the remaining suffix.  span p xs is equivalent to
 
-- (takeWhile p xs, dropWhile p xs), while break p uses the negation of p.
 

 
takeWhile               :: (a -> Bool) -> [a] -> [a]
 
takeWhile p []          =  []
 
takeWhile p (x:xs)
 
            | p x       =  x : takeWhile p xs
 
            | otherwise =  []


dropWhile               :: (a -> Bool) -> [a] -> [a]
 
dropWhile p []          =  []
 
dropWhile p xs@(x:xs')
 
            | p x       =  dropWhile p xs'
 
            | otherwise =  xs


span, break             :: (a -> Bool) -> [a] -> ([a],[a])
 
span p []            = ([],[])
 
span p xs@(x:xs')
 
            | p x       =  (x:ys,zs)
 
            | otherwise =  ([],xs)
 
                           where (ys,zs) = span p xs'


break p                 =  span (not . p)


-- lines breaks a string up into a list of strings at newline characters.
 
-- The resulting strings do not contain newlines.  Similary, words
 
-- breaks a string up into a list of words, which were delimited by
 
-- white space.  unlines and unwords are the inverse operations.
 
-- unlines joins lines with terminating newlines, and unwords joins
 
-- words with separating spaces.
 

 
lines            :: String -> [String]
 
lines ""         =  []
 
lines s          =  let (l, s') = break (== '\n') s
 
                      in  l : case s' of
 
                                []      -> []
 
                                (_:s'') -> lines s''


words            :: String -> [String]
 
words s          =  case dropWhile Char.isSpace s of
 
                      "" -> []
 
                      s' -> w : words s''
 
                            where (w, s'') = break Char.isSpace s'


unlines          :: [String] -> String
 
unlines          =  concatMap (++ "\n")


unwords          :: [String] -> String
 
unwords []       =  ""
 
unwords ws       =  foldr1 (\w s -> w ++ ' ':s) ws


-- reverse xs returns the elements of xs in reverse order.  xs must be finite.
 
reverse          :: [a] -> [a]
 
reverse          =  foldl (flip (:)) []


-- and returns the conjunction of a Boolean list.  For the result to be
 
-- True, the list must be finite; False, however, results from a False
 
-- value at a finite index of a finite or infinite list.  or is the
 
-- disjunctive dual of and.
 
and, or          :: [Bool] -> Bool
 
and              =  foldr (&&) True
 
or               =  foldr (||) False


-- Applied to a predicate and a list, any determines if any element
 
-- of the list satisfies the predicate.  Similarly, for all.
 
any, all         :: (a -> Bool) -> [a] -> Bool
 
any p            =  or . map p
 
all p            =  and . map p


-- elem is the list membership predicate, usually written in infix form,
 
-- e.g., x ‘elem‘ xs.  notElem is the negation.
 
elem, notElem    :: (Eq a) => a -> [a] -> Bool
 
elem x           =  any (== x)
 
notElem x        =  all (/= x)


-- lookup key assocs looks up a key in an association list.
 
lookup           :: (Eq a) => a -> [(a,b)] -> Maybe b
 
lookup key []    =  Nothing
 
lookup key ((x,y):xys)
 
    | key == x   =  Just y
 
    | otherwise  =  lookup key xys


-- sum and product compute the sum or product of a finite list of numbers.
 
sum, product     :: (Num a) => [a] -> a
 
sum              =  foldl (+) 0
 
product          =  foldl (⋆) 1


-- maximum and minimum return the maximum or minimum value from a list,
 
-- which must be non-empty, finite, and of an ordered type.
 
maximum, minimum :: (Ord a) => [a] -> a
 
maximum []       =  error "Prelude.maximum: empty list"
 
maximum xs       =  foldl1 max xs


minimum []       =  error "Prelude.minimum: empty list"
 
minimum xs       =  foldl1 min xs


-- zip takes two lists and returns a list of corresponding pairs.  If one
 
-- input list is short, excess elements of the longer list are discarded.
 
-- zip3 takes three lists and returns a list of triples.  Zips for larger
 
-- tuples are in the List library
 

 
zip              :: [a] -> [b] -> [(a,b)]
 
zip              =  zipWith (,)


zip3             :: [a] -> [b] -> [c] -> [(a,b,c)]
 
zip3             =  zipWith3 (,,)


-- The zipWith family generalises the zip family by zipping with the
 
-- function given as the first argument, instead of a tupling function.
 
-- For example, zipWith (+) is applied to two lists to produce the list
 
-- of corresponding sums.
 

 
zipWith          :: (a->b->c) -> [a]->[b]->[c]
 
zipWith z (a:as) (b:bs)
 
                 =  z a b : zipWith z as bs
 
zipWith _ _ _    =  []


zipWith3         :: (a->b->c->d) -> [a]->[b]->[c]->[d]
 
zipWith3 z (a:as) (b:bs) (c:cs)
 
                 =  z a b c : zipWith3 z as bs cs
 
zipWith3 _ _ _ _ =  []


-- unzip transforms a list of pairs into a pair of lists.
 

 
unzip            :: [(a,b)] -> ([a],[b])
 
unzip            =  foldr (\(a,b) ~(as,bs) -> (a:as,b:bs)) ([],[])


unzip3           :: [(a,b,c)] -> ([a],[b],[c])
 
unzip3           =  foldr (\(a,b,c) ~(as,bs,cs) -> (a:as,b:bs,c:cs))
 
                          ([],[],[])

9.2 Prelude — PreludeText (텍스트·읽기/보여주기 모듈)

여기서는 값을 문자열로 보여 주는 Show와, 문자열에서 값을 다시 읽어 들이는 Read, 그리고 그 바탕이 되는 ShowS, ReadS, lex 같은 저수준 도구들을 다뤄요. 기본 타입들은 deriving 절로 처리되거나 아래처럼 명시적인 인스턴스로 정의되어 있어요.

module PreludeText (
 
    ReadS, ShowS,
 
    Read(readsPrec, readList),
 
    Show(showsPrec, show, showList),
 
    reads, shows, read, lex,
 
    showChar, showString, readParen, showParen ) where


-- The instances of Read and Show for
 
--      Bool, Maybe, Either, Ordering
 
-- are done via "deriving" clauses in Prelude.hs
 

 
import Data.Char(isSpace, isAlpha, isDigit, isAlphaNum,
 
                 showLitChar, readLitChar, lexLitChar)


import Numeric(showSigned, showInt, readSigned, readDec, showFloat,
 
               readFloat, lexDigits)


type  ReadS a  = String -> [(a,String)]
 
type  ShowS    = String -> String


class  Read a  where
 
    readsPrec        :: Int -> ReadS a
 
    readList         :: ReadS [a]
 

 
        -- Minimal complete definition:
 
        --      readsPrec
 
    readList         = readParen False (\r -> [pr | ("[",s)  <- lex r,
 
                                                    pr       <- readl s])
 
                       where readl  s = [([],t)   | ("]",t)  <- lex s] ++
 
                                        [(x:xs,u) | (x,t)    <- reads s,
 
                                                    (xs,u)   <- readl' t]
 
                             readl' s = [([],t)   | ("]",t)  <- lex s] ++
 
                                        [(x:xs,v) | (",",t)  <- lex s,
 
                                                    (x,u)    <- reads t,
 
                                                    (xs,v)   <- readl' u]


class  Show a  where
 
    showsPrec        :: Int -> a -> ShowS
 
    show             :: a -> String
 
    showList         :: [a] -> ShowS
 

 
        -- Mimimal complete definition:
 
        --      show or showsPrec
 
    showsPrec _ x s   = show x ++ s
 

 
    show x            = showsPrec 0 x ""
 

 
    showList []       = showString "[]"
 
    showList (x:xs)   = showChar '[' . shows x . showl xs
 
                        where showl []     = showChar ']'
 
                              showl (x:xs) = showChar ',' . shows x .
 
                                             showl xs


reads            :: (Read a) => ReadS a
 
reads            =  readsPrec 0


shows            :: (Show a) => a -> ShowS
 
shows            =  showsPrec 0


read             :: (Read a) => String -> a
 
read s           =  case [x | (x,t) <- reads s, ("","") <- lex t] of
 
                         [x] -> x
 
                         []  -> error "Prelude.read: no parse"
 
                         _   -> error "Prelude.read: ambiguous parse"


showChar         :: Char -> ShowS
 
showChar         =  (:)


showString       :: String -> ShowS
 
showString       =  (++)


showParen        :: Bool -> ShowS -> ShowS
 
showParen b p    =  if b then showChar '(' . p . showChar ')' else p


readParen        :: Bool -> ReadS a -> ReadS a
 
readParen b g    =  if b then mandatory else optional
 
                    where optional r  = g r ++ mandatory r
 
                          mandatory r = [(x,u) | ("(",s) <- lex r,
 
                                                 (x,t)   <- optional s,
 
                                                 (")",u) <- lex t    ]


-- This lexer is not completely faithful to the Haskell lexical syntax.
 
-- Current limitations:
 
--    Qualified names are not handled properly
 
--    Octal and hexidecimal numerics are not recognized as a single token
 
--    Comments are not treated properly
 

 
lex              :: ReadS String
 
lex ""           =  [("","")]
 
lex (c:s)
 
   | isSpace c   =  lex (dropWhile isSpace s)
 
lex ('\'':s)     =  [('\'':ch++"'", t) | (ch,'\'':t)  <- lexLitChar s,
 
                                         ch /= "'" ]
 
lex ('"':s)      =  [('"':str, t)      | (str,t) <- lexString s]
 
                    where
 
                    lexString ('"':s) = [("\"",s)]
 
                    lexString s = [(ch++str, u)
 
                                         | (ch,t)  <- lexStrItem s,
 
                                           (str,u) <- lexString t  ]
 

 
                    lexStrItem ('\\':'&':s) =  [("\\&",s)]
 
                    lexStrItem ('\\':c:s) | isSpace c
 
                                           =  [("\\&",t) |
 
                                               '\\':t <-
 
                                                   [dropWhile isSpace s]]
 
                    lexStrItem s           =  lexLitChar s


lex (c:s) | isSingle c = [([c],s)]
 
          | isSym c    = [(c:sym,t)       | (sym,t) <- [span isSym s]]
 
          | isAlpha c  = [(c:nam,t)       | (nam,t) <- [span isIdChar s]]
 
          | isDigit c  = [(c:ds++fe,t)    | (ds,s)  <- [span isDigit s],
 
                                            (fe,t)  <- lexFracExp s     ]
 
          | otherwise  = []    -- bad character
 
             where
 
              isSingle c =  c ‘elem‘ ",;()[]{}_‘"
 
              isSym c    =  c ‘elem‘ "!@#$%&⋆+./<=>?\\^|:-~"
 
              isIdChar c =  isAlphaNum c || c ‘elem‘ "_'"
 

 
              lexFracExp ('.':c:cs) | isDigit c
 
                            = [('.':ds++e,u) | (ds,t) <- lexDigits (c:cs),
 
                                               (e,u)  <- lexExp t]
 
              lexFracExp s  = lexExp s
 

 
              lexExp (e:s) | e ‘elem‘ "eE"
 
                       = [(e:c:ds,u) | (c:t)  <- [s], c ‘elem‘ "+-",
 
                                                 (ds,u) <- lexDigits t] ++
 
                         [(e:ds,t)   | (ds,t) <- lexDigits s]
 
              lexExp s = [("",s)]


instance  Show Int  where
 
    showsPrec n = showsPrec n . toInteger
 
        -- Converting to Integer avoids
 
        -- possible difficulty with minInt


instance  Read Int  where
 
  readsPrec p r = [(fromInteger i, t) | (i,t) <- readsPrec p r]
 
        -- Reading at the Integer type avoids
 
        -- possible difficulty with minInt


instance  Show Integer  where
 
    showsPrec           = showSigned showInt


instance  Read Integer  where
 
    readsPrec p         = readSigned readDec


instance  Show Float  where
 
    showsPrec p         = showFloat


instance  Read Float  where
 
    readsPrec p         = readSigned readFloat


instance  Show Double  where
 
    showsPrec p         = showFloat


instance  Read Double  where
 
    readsPrec p         = readSigned readFloat


instance  Show ()  where
 
    showsPrec p () = showString "()"


instance Read () where
 
    readsPrec p    = readParen False
 
                            (\r -> [((),t) | ("(",s) <- lex r,
 
                                             (")",t) <- lex s ] )
 
instance  Show Char  where
 
    showsPrec p '\'' = showString "'\\''"
 
    showsPrec p c    = showChar '\'' . showLitChar c . showChar '\''
 

 
    showList cs = showChar '"' . showl cs
 
                 where showl ""       = showChar '"'
 
                       showl ('"':cs) = showString "\\\"" . showl cs
 
                       showl (c:cs)   = showLitChar c . showl cs


instance  Read Char  where
 
    readsPrec p      = readParen False
 
                            (\r -> [(c,t) | ('\'':s,t)<- lex r,
 
                                            (c,"\'")  <- readLitChar s])
 

 
    readList = readParen False (\r -> [(l,t) | ('"':s, t) <- lex r,
 
                                               (l,_)      <- readl s ])
 
        where readl ('"':s)      = [("",s)]
 
              readl ('\\':'&':s) = readl s
 
              readl s            = [(c:cs,u) | (c ,t) <- readLitChar s,
 
                                               (cs,u) <- readl t       ]


instance  (Show a) => Show [a]  where
 
    showsPrec p      = showList


instance  (Read a) => Read [a]  where
 
    readsPrec p      = readList


-- Tuples
 

 
instance  (Show a, Show b) => Show (a,b)  where
 
    showsPrec p (x,y) = showChar '(' . shows x . showChar ',' .
 
                                       shows y . showChar ')'


instance  (Read a, Read b) => Read (a,b)  where
 
    readsPrec p       = readParen False
 
                            (\r -> [((x,y), w) | ("(",s) <- lex r,
 
                                                 (x,t)   <- reads s,
 
                                                 (",",u) <- lex t,
 
                                                 (y,v)   <- reads u,
 
                                                 (")",w) <- lex v ] )


-- Other tuples have similar Read and Show instances
 

 

9.3 Prelude — PreludeIO (입출력 모듈)

입출력은 순수한 함수형 언어에서 특히 까다로운 부분이죠. 이 모듈은 IO 타입과 함께, 화면에 쓰고(putStr, print) 읽는(getLine, readLn) 기본 함수들, 그리고 파일 입출력(readFile, writeFile)까지 제공해요. 대부분의 함수가 시스템 프리미티브(prim...)에 그대로 연결되어 있다는 걸 보면, 입출력 동작 하나하나가 사실상 컴파일러에 맡겨져 있음을 알 수 있어요.

module PreludeIO (
 
    FilePath, IOError, ioError, userError, catch,
 
    putChar, putStr, putStrLn, print,
 
    getChar, getLine, getContents, interact,
 
    readFile, writeFile, appendFile, readIO, readLn
 
  ) where


import PreludeBuiltin


type  FilePath = String


data IOError    -- The internals of this type are system dependent


instance  Show IOError  where ...
 
instance  Eq IOError  where ...


ioError    ::  IOError -> IO a
 
ioError    =   primIOError


userError  ::  String -> IOError
 
userError  =   primUserError


catch      ::  IO a -> (IOError -> IO a) -> IO a
 
catch      =   primCatch


putChar    :: Char -> IO ()
 
putChar    =  primPutChar


putStr     :: String -> IO ()
 
putStr s   =  mapM_ putChar s


putStrLn   :: String -> IO ()
 
putStrLn s =  do putStr s
 
                 putStr "\n"


print      :: Show a => a -> IO ()
 
print x    =  putStrLn (show x)


getChar    :: IO Char
 
getChar    =  primGetChar


getLine    :: IO String
 
getLine    =  do c <- getChar
 
                 if c == '\n' then return "" else
 
                    do s <- getLine
 
                       return (c:s)


getContents :: IO String
 
getContents =  primGetContents


interact    ::  (String -> String) -> IO ()
 
-- The hSetBuffering ensures the expected interactive behaviour
 
interact f  =  do hSetBuffering stdin  NoBuffering
 
                  hSetBuffering stdout NoBuffering
 
                  s <- getContents
 
                  putStr (f s)


readFile   :: FilePath -> IO String
 
readFile   =  primReadFile


writeFile  :: FilePath -> String -> IO ()
 
writeFile  =  primWriteFile


appendFile :: FilePath -> String -> IO ()
 
appendFile =  primAppendFile
 

 
  -- raises an exception instead of an error
 
readIO   :: Read a => String -> IO a
 
readIO s =  case [x | (x,t) <- reads s, ("","") <- lex t] of
 
              [x] -> return x
 
              []  -> ioError (userError "Prelude.readIO: no parse")
 
              _   -> ioError (userError "Prelude.readIO: ambiguous parse")


readLn :: Read a => IO a
 
readLn =  do l <- getLine
 
             r <- readIO l
 
             return r

더 알아보기 (Learn more)

  • 전체 보고서 보기 — Haskell 2010 Language Report의 다른 챕터도 온라인판에서 함께 읽을 수 있어요: https://www.haskell.org/onlinereport/haskell2010/
  • 실제 컴파일러의 프렐류드 — 여기 명세보다 훨씬 상세하고, 성능을 고려한 구현은 GHC의 Prelude(GHC.Base, Data.List, GHC.Show 등) 소스를 참고하면 좋아요.
  • 이어서 읽기 — 프렐류드가 사용하는 라이브러리 모듈(Data.Char, Control.Monad, System.IO, Numeric)의 자세한 설명은 이 보고서의 Part II에 있어요.