Nim can be Haskell  This is an example from the real project. This code reads 
application config from json with defaults handling in functional style. It 
uses `data` macro from the `nimboost` library to generate immutable data 
structures, constructors, `copy` operators, and functional types and control 
structures from `nimfp` library.
    
    
    import json,
           future,
           fp,
           boost.typeutils
    
    data HttpConfig, exported, copy:
      host: string
      port: int
    data HttpConfigDef, exported:
      host = "localhost".some
      port = 8280.some
    
    data SmtpConfig, exported:
      host: string
      port: Option[int]
      credentials: Option[tuple[user: string, pass: string]]
      emailFrom: string
      emailTo: string
    data SmtpConfigDef, exported:
      host = string.none
      port = int.none.some
      credentials = none(tuple[user: string, pass: string]).some
      emailFrom = string.none
      emailTo = string.none
    
    data DbConfig, exported:
      host: string
      port: int
      dbName: string
      user: Option[string]
      password: Option[string]
      createTables: bool
    data DbConfigDef, exported:
      host = "localhost".some
      port = 5432.some
      dbName = string.none
      user = string.none.some
      password = string.none.some
      createTables = true.some
    
    data LogConfig, exported, copy:
      file: Option[string]
      debug: bool
    data LogConfigDef, exported:
      file = string.none.some
      debug = false.some
    
    data ServiceConfig, exported:
      http: HttpConfig
      db: DbConfig
      smtp: Option[SmtpConfig]
      log: LogConfig
    data ServiceConfigDef, exported:
      http = initHttpConfigDef()
      db = initDbConfigDef()
      smtp = initSmtpConfigDef()
      log = initLogConfigDef()
    
    proc defaultConfig: ServiceConfigDef = initServiceConfigDef()
    
    proc getConfig(d: HttpConfigDef): Option[HttpConfig] = act do:
      h <- d.host
      p <- d.port
      yield initHttpConfig(host = h, port = p)
    
    proc getConfig(d: SmtpConfigDef): Option[SmtpConfig] = act do:
      h <- d.host
      p <- d.port
      c <- d.credentials
      f <- d.emailFrom
      t <- d.emailTo
      yield initSmtpConfig(host = h, port = p, credentials = c, email>From = f, 
emailTo = t)
    
    proc getConfig(d: DbConfigDef): Option[DbConfig] = act do:
      h <- d.host
      p <- d.port
      db <- d.dbName
      user <- d.user
      pass <- d.password
      createTables <- d.createTables
      yield initDbConfig(host = h, port = p, dbName = db, user = user, password 
= pass, createTables = createTables)
    
    proc getConfig(d: LogConfigDef): Option[LogConfig] = act do:
      file <- d.file
      debug <- d.debug
      yield initLogConfig(file = file, debug = debug)
    
    proc getConfig(d: ServiceConfigDef): Option[ServiceConfig] = act do:
      http <- d.http.getConfig
      db <- d.db.getConfig
      smtp <- d.smtp.getConfig
      log <- d.log.getConfig
      yield initServiceConfig(http = http, db = db, smtp = smtp.some, log = log)
    
    template getOption(n: JsonNode, name: untyped): untyped =
      mixin defValue
      const notFound = "Required parameter \"" & astToStr(name) & "\" not found"
      when type(defValue.name.elemType) is Option:
        var x: defValue.name.elemType
        (n.some.rightS >>= (mget(astToStr(name)) >=> mvalue(type(x.get))))
        .optionT.map((v: type(x.get)) => v.some).getOrElseF(() => 
defValue.name.asEither(notFound))
      else:
        (n.some.rightS >>= (mget(astToStr(name)) >=> 
mvalue(defValue.name.elemType)))
        .optionT.getOrElseF(() => defValue.name.asEither(notFound))
    
    template getOptionT(n: JsonNode, name: untyped): untyped =
      mixin defValue
      n.getOption(name).map((v: type(defValue.name.get)) => v.some).optionT
    
    proc parseHttpConfig(
      node: Option[JsonNode],
      defValue: HttpConfigDef
    ): EitherS[HttpConfig] =
      node.map do(n: JsonNode) -> auto:
        act do:
          host <- n.getOption(host)
          port <- n.getOption(port)
          yield initHttpConfig(host = host, port = port)
      .getOrElse(defValue.getConfig.asEither("HTTP configuration not found"))
    
    proc parseDbConfig(
      node: Option[JsonNode],
      defValue: DbConfigDef
    ): EitherS[DbConfig] =
      node.map do(n: JsonNode) -> auto:
        act do:
          host <- n.getOption(host)
          port <- n.getOption(port)
          dbName <- n.getOption(dbName)
          user <- n.getOption(user)
          password <- n.getOption(password)
          createTables <- n.getOption(createTables)
          yield initDbConfig(host = host, port = port, dbName = dbName, user = 
user, password = password, createTables = createTables)
      .getOrElse(defValue.getConfig.asEither("Database configuration not 
found"))
    
    proc parseSmtpConfig*(
      node: Option[JsonNode],
      defValue: SmtpConfigDef
    ): EitherS[Option[SmtpConfig]] =
      node.map do(n: JsonNode) -> auto:
        act do:
          host <- n.getOption(host)
          port <- n.getOption(port)
          credentials <- act do:
            u <- n.mget("user").optionT.flatMapF((v: JsonNode) => value(string, 
v))
            p <- n.mget("pass").optionT.flatMapF((v: JsonNode) => value(string, 
v))
            yield (user: u, pass: p)
          .run
          emailFrom <- n.getOption(emailFrom)
          emailTo <- n.getOption(emailTo)
          yield initSmtpConfig(host = host, port = port, credentials = 
credentials, emailFrom = emailFrom, emailTo = emailTo)
      .sequence
    
    proc parseLogConfig(
      node: Option[JsonNode],
      defValue: LogConfigDef
    ): EitherS[LogConfig] =
      node.map do(n: JsonNode) -> auto:
        act do:
          file <- n.getOption(file)
          debug <- n.getOption(debug)
          yield initLogConfig(file = file, debug = debug)
      .getOrElse(defValue.getConfig.asEither("Log configuration not found"))
    
    proc parseConfig*(
      node: Option[JsonNode],
      defValue: ServiceConfigDef = defaultConfig(),
      overrideHttpHost = string.none,
      overrideHttpPort = int.none,
      overrideLogFile = string.none,
      overrideLogDebug = bool.none
    ): EitherS[ServiceConfig] = act do:
      httpNode <- node.mget("http")
      http <- act do:
        h1 <- parseHttpConfig(httpNode, defValue.http).mapLeft(e => "HTTP 
configuration: " & e)
        h2 <- overrideHttpHost.map(v => h1.copyHttpConfig(host = 
v)).getOrElse(h1).rightS
        yield overrideHttpPort.map(v => h2.copyHttpConfig(port = 
v)).getOrElse(h2)
      dbNode <- node.mget("db")
      db <- parseDbConfig(dbNode, defValue.db).mapLeft(e => "Database 
configuration: " & e)
      smtpNode <- node.mget("smtp")
      smtp <- parseSmtpConfig(smtpNode, defValue.smtp).mapLeft(e => "SMTP 
configuration: " & e)
      logNode <- node.mget("log")
      log <- act do:
        l1 <- parseLogConfig(logNode, defValue.log).mapLeft(e => "Log 
configuration: " & e)
        l2 <- overrideLogFile.map(v => l1.copyLogConfig(file = 
v.some)).getOrElse(l1).rightS
        yield overrideLogDebug.map(v => l2.copyLogConfig(debug = 
v)).getOrElse(l2)
      yield initServiceConfig(http = http, db = db, smtp = smtp, log = log)
    

Reply via email to