Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Haskell Dictionary
  3. Function Composition (. operator)

Function Composition (. operator)

In Haskell, the . operator lets you compose multiple functions into a new function. (f . g) x means the same as f (g x) — it expresses "apply g first, then pass the result to f" as a single function. The $ operator is the function application operator. It has the lowest precedence and is right-associative, so you can use it in place of parentheses to make expressions easier to read. f $ g $ h x means the same as f (g (h x)). Combining these two operators lets you write declarative, "point-free style" code that is idiomatic Haskell.

Syntax

-- -----------------------------------------------
--  . operator (function composition)
-- -----------------------------------------------

-- Type signature: (.) :: (b -> c) -> (a -> b) -> a -> c
-- Composes two functions and returns a new function
-- (f . g) x  =  f (g x)

-- Example: compose negate and abs to create a function that
-- takes the absolute value and then negates it
-- negateAbs :: Int -> Int
-- negateAbs = negate . abs   -- negateAbs (-5)  →  -5

-- You can chain more than two functions (applied right to left)
-- pipeline :: String -> String
-- pipeline = reverse . map toUpper . filter (/= ' ')

-- -----------------------------------------------
--  $ operator (function application / fewer parentheses)
-- -----------------------------------------------

-- Type signature: ($) :: (a -> b) -> a -> b
-- The result is the same as regular function application,
-- but $ has the lowest precedence (0) and is right-associative
-- f $ x  =  f x

-- When parentheses become deeply nested, replacing them with $ improves readability
-- With parentheses:  putStrLn (show (negate (abs (-42))))
-- With $:            putStrLn $ show $ negate $ abs (-42)

-- -----------------------------------------------
--  Combining . and $ (point-free style)
-- -----------------------------------------------

-- Defining a function without naming its argument is called point-free style
-- Point-ful (x is explicit):
--   process x = putStrLn (show (negate x))
--
-- Point-free (composed with . and applied with $):
--   process = putStrLn . show . negate

Syntax Reference

Operator / PatternType Signature (summary)Description
(f . g) x(b -> c) -> (a -> b) -> a -> cApplies g first, then passes the result to f. Equivalent to f (g x).
f . g . hChain composition of multiple functionsFunctions are applied right to left. Equivalent to f (g (h x)).
f $ x(a -> b) -> a -> bFunction application operator. Its lowest precedence (0) lets you eliminate parentheses.
f $ g $ h xRight-associative chain of function applicationEquivalent to f (g (h x)).
f . g $ xCompose then applyBecause . has higher precedence than $, f . g is composed first and then applied to x.
Point-free styleDefinition without naming argumentsA style where process x = f (g x) is written as process = f . g.
ida -> aThe identity function. f . id = f and id . f = f hold, so it acts as the identity element for composition.

Sample Code

sg_compose_basic.hs
-- sg_compose_basic.hs — demonstrates the basics of the . and $ operators
-- Uses character data from Steins;Gate to explore
-- function composition and point-free style
--
--   Compile and run:
--     ghc sg_compose_basic.hs -o sg_compose_basic && ./sg_compose_basic

module Main where

import Data.Char (toUpper, toLower)

-- -----------------------------------------------
--  . operator: basic function composition
-- -----------------------------------------------

-- Compose a function that converts a string to uppercase and then reverses it
-- Functions are applied right to left: map toUpper first, then reverse
reverseUpper :: String -> String
reverseUpper = reverse . map toUpper
-- reverseUpper "Hououin Kyouma"  →  "AMUOYK NIUOUOH"

-- Compose a function that doubles the length of a string
-- length gets the character count, then (* 2) doubles it
doubleLength :: String -> Int
doubleLength = (* 2) . length
-- doubleLength "Makise Kurisu"  →  26

-- -----------------------------------------------
--  $ operator: reducing parentheses
-- -----------------------------------------------

-- Written with parentheses (tends to nest deeply)
withParens :: String -> String
withParens name = reverse (map toUpper (name ++ "!"))

-- Written with $ (reads naturally from left to right)
withDollar :: String -> String
withDollar name = reverse . map toUpper $ name ++ "!"
-- Combining . and $: first builds name ++ "!",
-- then passes it to the composed function reverse . map toUpper

-- -----------------------------------------------
--  Point-free style (omitting the argument)
-- -----------------------------------------------

-- Point-ful (argument name is explicit)
greetPointwise :: String -> String
greetPointwise name = "Hello, " ++ reverse (map toUpper name)

-- Point-free (composed with ., argument omitted)
-- Haskell's currying ensures this works correctly without the explicit argument
greetPointFree :: String -> String
greetPointFree = ("Hello, " ++) . reverse . map toUpper

-- -----------------------------------------------
--  Entry point
-- -----------------------------------------------
main :: IO ()
main = do
  putStrLn "===== . operator: function composition ====="
  -- Apply reverseUpper to each lab member's name
  putStrLn $ "reverseUpper \"Hououin Kyouma\" = " ++ reverseUpper "Hououin Kyouma"
  putStrLn $ "reverseUpper \"Makise Kurisu\"  = " ++ reverseUpper "Makise Kurisu"
  putStrLn $ "doubleLength \"Shiina Mayuri\"  = " ++ show (doubleLength "Shiina Mayuri")
  putStrLn $ "doubleLength \"Hashida Itaru\"  = " ++ show (doubleLength "Hashida Itaru")

  putStrLn ""
  putStrLn "===== $ operator: reducing parentheses ====="
  -- withParens and withDollar return the same result
  putStrLn $ "withParens  \"Urushibara Ruka\" = " ++ withParens  "Urushibara Ruka"
  putStrLn $ "withDollar  \"Urushibara Ruka\" = " ++ withDollar  "Urushibara Ruka"

  putStrLn ""
  putStrLn "===== Point-free style ====="
  -- Point-ful and point-free return the same result
  putStrLn $ "pointwise  \"Hououin Kyouma\" = " ++ greetPointwise "Hououin Kyouma"
  putStrLn $ "pointFree  \"Hououin Kyouma\" = " ++ greetPointFree "Hououin Kyouma"
ghc sg_compose_basic.hs -o sg_compose_basic && ./sg_compose_basic
[1 of 1] Compiling Main             ( sg_compose_basic.hs, sg_compose_basic.o )
Linking sg_compose_basic ...
===== . operator: function composition =====
reverseUpper "Hououin Kyouma" = AMUOYK NIUOUOH
reverseUpper "Makise Kurisu"  = USIRUК ESIKAM
doubleLength "Shiina Mayuri"  = 26
doubleLength "Hashida Itaru"  = 26

===== $ operator: reducing parentheses =====
withParens  "Urushibara Ruka" = !AKUR ARABIHS URU
withDollar  "Urushibara Ruka" = !AKUR ARABIHS URU

===== Point-free style =====
pointwise  "Hououin Kyouma" = Hello, AMUOYK NIUOUOH
pointFree  "Hououin Kyouma" = Hello, AMUOYK NIUOUOH
sg_compose_pipeline.hs
-- sg_compose_pipeline.hs — demonstrates pipeline processing with function composition
-- Transforms and formats lab member data from Steins;Gate
-- using a pipeline built with . and $
--
--   Compile and run:
--     ghc sg_compose_pipeline.hs -o sg_compose_pipeline && ./sg_compose_pipeline

module Main where

import Data.Char (toUpper)
import Data.List (intercalate, sortBy)
import Data.Ord  (comparing, Down(..))

-- -----------------------------------------------
--  Data type definition
-- -----------------------------------------------
-- Represents a single lab member
data LabMember = LabMember
  { memberNo   :: Int     -- Lab member number
  , memberName :: String  -- Name
  , iq         :: Int     -- IQ
  , isActive   :: Bool    -- Current participation status
  } deriving (Show)

-- -----------------------------------------------
--  Sample data
-- -----------------------------------------------
-- List of lab members at the Future Gadget Laboratory
labMembers :: [LabMember]
labMembers =
  [ LabMember { memberNo = 1, memberName = "Hououin Kyouma",  iq = 170, isActive = True  }
  , LabMember { memberNo = 2, memberName = "Makise Kurisu",   iq = 190, isActive = True  }
  , LabMember { memberNo = 3, memberName = "Shiina Mayuri",   iq =  90, isActive = True  }
  , LabMember { memberNo = 4, memberName = "Hashida Itaru",   iq = 168, isActive = True  }
  , LabMember { memberNo = 9, memberName = "Urushibara Ruka", iq = 110, isActive = False }
  ]

-- -----------------------------------------------
--  Individual transformation functions (pipeline pieces)
-- -----------------------------------------------

-- Filters to only active members
filterActive :: [LabMember] -> [LabMember]
filterActive = filter isActive

-- Sorts members by IQ in descending order
sortByIQ :: [LabMember] -> [LabMember]
sortByIQ = sortBy (comparing (Down . iq))

-- Formats a single member as a string
formatMember :: LabMember -> String
formatMember m =
  "No." ++ pad3 (memberNo m)
  ++ "  " ++ padRight 18 (memberName m)
  ++ "  IQ: " ++ padLeft 3 (show (iq m))
  where
    -- Zero-pads the number to 3 digits
    pad3 n
      | n < 10    = "00" ++ show n
      | n < 100   = "0"  ++ show n
      | otherwise =         show n
    -- Right-pads a string with spaces to the given width
    padRight n s = s ++ replicate (max 0 (n - length s)) ' '
    -- Left-pads a string with spaces to the given width
    padLeft  n s = replicate (max 0 (n - length s)) ' ' ++ s

-- -----------------------------------------------
--  Pipeline function composed with .
-- -----------------------------------------------

-- A pipeline that filters active members, sorts by IQ, and formats each one
-- Applied right to left: filterActive → sortByIQ → map formatMember
buildReport :: [LabMember] -> [String]
buildReport = map formatMember . sortByIQ . filterActive

-- -----------------------------------------------
--  Output with parentheses reduced using $
-- -----------------------------------------------

-- Assembles the full report string with a header
-- $ avoids deeply nested calls to putStrLn
printReport :: [LabMember] -> IO ()
printReport members = do
  putStrLn "===== Active Lab Members (by IQ) ====="
  -- $ lets you write unlines and buildReport without nesting
  putStr $ unlines . buildReport $ members
  putStrLn $ "Total: " ++ show (length (filterActive members)) ++ " members"

-- -----------------------------------------------
--  Entry point
-- -----------------------------------------------
main :: IO ()
main = do
  printReport labMembers

  putStrLn ""
  putStrLn "===== All Lab Members (with status) ====="
  -- Passes a composed function to mapM_ to print each line
  mapM_ (putStrLn . formatWithStatus) labMembers

  putStrLn ""
  putStrLn "===== Names only (point-free) ====="
  -- Composes map memberName and intercalate ", " with .
  putStrLn . intercalate ", " . map memberName $ labMembers
  where
    -- Formats a member with their participation status
    formatWithStatus m =
      formatMember m ++ "  Status: " ++ if isActive m then "Active" else "Inactive"
      where
        -- Locally redefines formatMember to reuse its helper functions
        formatMember mm =
          "No." ++ pad3 (memberNo mm)
          ++ "  " ++ padRight 18 (memberName mm)
          ++ "  IQ: " ++ padLeft 3 (show (iq mm))
        pad3 n
          | n < 10    = "00" ++ show n
          | n < 100   = "0"  ++ show n
          | otherwise =         show n
        padRight n s = s ++ replicate (max 0 (n - length s)) ' '
        padLeft  n s = replicate (max 0 (n - length s)) ' ' ++ s
ghc sg_compose_pipeline.hs -o sg_compose_pipeline && ./sg_compose_pipeline
[1 of 1] Compiling Main             ( sg_compose_pipeline.hs, sg_compose_pipeline.o )
Linking sg_compose_pipeline ...
===== Active Lab Members (by IQ) =====
No.002  Makise Kurisu       IQ: 190
No.001  Hououin Kyouma      IQ: 170
No.004  Hashida Itaru       IQ: 168
No.003  Shiina Mayuri       IQ:  90
Total: 4 members

===== All Lab Members (with status) =====
No.001  Hououin Kyouma      IQ: 170  Status: Active
No.002  Makise Kurisu       IQ: 190  Status: Active
No.003  Shiina Mayuri       IQ:  90  Status: Active
No.004  Hashida Itaru       IQ: 168  Status: Active
No.009  Urushibara Ruka     IQ: 110  Status: Inactive

===== Names only (point-free) =====
Hououin Kyouma, Makise Kurisu, Shiina Mayuri, Hashida Itaru, Urushibara Ruka

Overview

The . operator (function composition) in Haskell has type (.) :: (b -> c) -> (a -> b) -> a -> c. It composes two functions and returns a new function. (f . g) x means the same as f (g x), with functions applied from right to left. You can chain three or more functions with f . g . h to express a data transformation pipeline in a declarative style. The $ operator (function application) has type ($) :: (a -> b) -> a -> b. The result is identical to ordinary function application, but its lowest precedence (0) and right-associativity let you replace deeply nested parentheses — writing f $ g $ h x instead of f (g (h x)). Combining . and $ as in f . g $ x lets you express the natural flow of "compose functions first, then pass a value at the end." Point-free style — defining functions without naming their arguments — is achieved by composing with . and is an idiomatic Haskell way to express concise processing pipelines. For combining these operators with higher-order functions, see Higher-Order Functions (map / filter / foldr). For lambda expressions and partial application, see Basic Function Definitions.

If you find any errors or copyright issues, please .