Говно

#!/usr/bin/env runhaskell

{-# LANGUAGE NamedFieldPuns #-}

module Logger (
  Severity(Fatal, Error, Warn, Info, Debug)
, Logger(FileLogger)
, log
, runLogger

, main -- example
) where

import Prelude hiding (log)

import Control.Monad
import Control.Monad.Trans.Class

import qualified Control.Monad.Trans.State as State -- from mtl-2.2.1
import qualified System.IO as IO

data Severity = Fatal | Error | Warn | Info | Debug deriving (Eq, Ord)

data Logger = FileLogger {
  device :: IO.Handle
, severity :: Severity
, progname :: String
}

log :: Severity -> String -> State.StateT Logger IO ()
log severity' text = do
  FileLogger{severity, device} <- State.get
  when (severity >= severity') $
    lift $ IO.hPutStr device $ text ++ "\n"

runLogger :: Logger -> State.StateT Logger IO () -> IO ()
runLogger = flip State.evalStateT

--- example

main :: IO ()
main = runLogger logger $ do
  log Debug "debug disabled"
  State.state $ \ logger -> ((), logger{severity = Debug})
  log Debug "debug enabled"
  where
    logger = FileLogger { device=IO.stdout, severity=Info, progname="update"}

Охуенно

#!/usr/bin/env runhaskell

{-# LANGUAGE NamedFieldPuns #-}

module Logger (
  Severity(Fatal, Error, Warn, Info, Debug)
, Logger(FileLogger)
, log
, runLogger

, main -- example
) where

import Prelude hiding (log)

import Control.Monad
import Control.Monad.Trans.Class

import qualified Control.Monad.Trans.Reader as Reader -- from mtl-2.2.1
import qualified System.IO as IO

data Severity = Fatal | Error | Warn | Info | Debug deriving (Eq, Ord)

data Logger = FileLogger {
  device :: IO.Handle
, severity :: Severity
}

log :: Severity -> String -> Reader.ReaderT Logger IO ()
log severity' text = do
  FileLogger{severity, device} <- Reader.ask
  when (severity >= severity') $
    lift $ IO.hPutStr device $ text ++ "\n"

runLogger :: Logger -> Reader.ReaderT Logger IO () -> IO ()
runLogger = flip Reader.runReaderT


--- example

main :: IO ()
main = runLogger logger $ do
  log Debug "debug disabled"
  Reader.withReaderT (\logger -> logger{severity = Debug}) $
    log Debug "debug enabled"
  where
    logger = FileLogger { device=IO.stdout, severity=Info}

Это на самом деле не пост про хаскель,

Просто на нем все выглядит понятно и доходчиво. А теперь представьте себе, что у вас есть язык с сайд-эффектами. Так какого хрена вы пишете

I18n.locale = :ru

, а не

I18n.with_locale(:ru) do

?????

На самом деле я пригорел даже не с руби,

что само по себе редкий случай, а с flow, написанного, на минуточку, на ocaml. Который типа функциональный и все такое. Почему там стейт ебаного логгера управляется так?

И эти люди еще хотят,

чтобы компилятор сам решал когда можно пустить в несколько потоков, а когда нет. Пиздец

Да, этот пост про

haskell, ocaml, ruby и js одновременно. Привыкайте, епт

Мини-анонс

Скоро (возможно даже в этом году) будет пост про динамические переменные. Рекомендую не пропустить, там будет про охуеннейшую штуку, которую почему то не завезли в “мейнстримные” скриптовые языки. В хаскеле, понятное дело, оно имеет не больше смысла, чем в эрланге, но все равно стоит почитать. Да, это что то типа withReaderT

PS

да, поцоны, я пишу логгер без монады Writer. Почему? Идите нахуй, вот почему