Skip to content

Nim for Haskell Programmers

林亦恩 edited this page Jan 31, 2022 · 10 revisions
DISCLAIMER!

This page is unofficial and incomplete. Contributions are appreciated.

official doc

Language Features

Feature Haskell Nim
Comments -- single line, {- multiline -}(nestable) # single line, #[multiline]# (nestable)
Blocks Uses space and tab or C-like Uses indents like Python, another option is statement list expression
Operators operator is function (use (+) a b , or a + b), Operator has precedence, infix by default, Unicode syntax command call syntax unicode operators
Operator overloading None Operators are user defined except = and ., can overload: subscripts, curly subscripts a{b}, experimental call and dot operators
If/else statement None if a: foo() else: bar()
If/else expression if test then a else b if test: a else: b
case expression case t of m1 -> a
otherwise -> b
case t
of m1: a
else: b
Exception many ways, use Control.Monad, try: foo() except Exception as ex: bar() finally: bar() - can omit as ex or Exception as ex
Procedure definition id a = a, id::a->a for declare proc foo(a: U, b: S): T = discard in module
Method definition None method foo(obj: Obj, a: U, b: S): T = discard in module
Calling procedure func a b, or a \func` b` foo(a, b), foo a, b, a.foo(b), a.foo b
Calling method None foo(obj, a, b), foo obj, a, b, obj.foo(a, b), obj.foo a, b
Method/procedure declarations are order-agnostic Yes No, can use forward declarations, experimental code reordering
String literals "str" "str", """str""", foo"str" (raw string literals)
Collection literals list comprehension `[(a, b) b <- ['a'..'z']`, a <- [1..50], even a], Overloaded string and list
compiler output hi interface file, native assembly or llvm translate to C, C++, Objective C , JavaScript
major compiler ghc nim
major REPL ghci nim secret
stability old young
meta-programming template-haskell macro keyword
template use {-# LANGUAGE CPP #-} template keyword
pure function IO Monad func keyword or noSideEffect pragma

Data Types

Haskell

Haskell is pure functional language, variables are immutable

However, you can use State Monad or IORef to behave like mutable

let behaves like create new immutable variable

quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
  let smaller = quicksort [a | a<-xs, a<=x]
      bigger  = quicksort [a | a<-xs, a>x]
  in  smaller ++ [x] ++ bigger

In addition, where is the same

quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) = smaller ++ [x] ++ bigger
    where
       smaller = quicksort [a | a<-xs, a<=x]
       bigger  = quicksort [a | a<-xs, a>x]

Nim

var for mutable

let for immutable

const for compile-time symbol

var mutable = "some"
mutable &= " string"
let shadow_copy = mutable
const flags = ["--run", "--hints:off"]

Haskell and Nim

Haskell Nim
Int8 int8
Int16 int16
Int32 int32
Int64 int64
Word8 uint8
Word16 uint16
Word32 uint32
Word64 uint64
Ptr pointer
Bool bool

Only Haskell

Haskell meaning
Integer Arbitrary precision integers
Double Double-precision floating point numbers
Float Single-precision floating point numbers
Ratio a numerator and denominator in type a
Rational aka Ratio Integer
Char represent Unicode code points
Complex a real and image number in type a
[a] List with element in type a
String aka [Char]

Only Nim

Nim meaning
char 1 byte character
string mutable chars
cstring pointer to memory, const char*
float float number
float32 32 bit float
float64 64 bit float
ptr[T] untraced pointer
ref[T] traced pointer
byte aka uint8

Nim has many type to interfacing with C

for Example: csize_t, cint, cshort

type inference

Both Haskell and Nim is static typed

Haskell's most widely used compiler (GHC) has strong type inference to determine what expression's type is

for Example

Prelude> :t 25
25:: Num a => a
Prelude> add a b = a+b
Prelude> :t add
add :: Num a => a -> a -> a
Prelude> add 1 2
3
Prelude> add 1.0 2.0
3.0
Prelude> (add 1 2)::Int
3
Prelude> (add 1 2)::Integer
3
Prelude> (add 1 2)::Float
3.0
Prelude> (add 1 2)::Double
3.0

the + function' signature is Num a => a -> a -> a, so add's parameter a and b must instance of Num Int, Integer ,Float, Double are instances of Num type class use ::Type to explicit tell the compiler the type of expression

Prelude> :{
Prelude| repr::(Show a)=>a->String
Prelude| repr a = "repr " ++ (show a)
Prelude| :}
Prelude> repr 23
"repr 23"
Prelude> repr [1..5]
"repr [1,2,3,4,5]"
Prelude> repr (return ()::IO ())

<interactive>:7:1: error:
    * No instance for (Show (IO ())) arising from a use of `repr'
    * In the expression: repr (return () :: IO ())
      In an equation for `it': it = repr (return () :: IO ())

show's signature is Show a->String, we can say repr's parameter a must instance of Show typeclass

Prelude> a = []
Prelude> :t a
a :: [a]
Prelude> a ++ ['a'..'d']
"abcd"
Prelude> a ++ [1..5]
[1,2,3,4,5]

a :: [a] means a can be any type

Nim don't accept any type,

var arr: seq[int]
for i in 0..5:
  arr.add i
echo $arr

Documentation, libraries & popular tooling

Feature Haskell Nim
IDE support use Haskell Language Server, see VS Code, see editor support
Package manager cabal, stack, ghc-pkg Nimble
Library format hs source, hi interface, o object, see Source code, unused code is not included in binary (dead code elimination), can also compile to a shared library or static library
Style guide see NEP-1
Doc generator haddock nim doc, nim rst2html, nim tex, nim jsondoc, nim doc2tex
Unit testing HTF QuickCheck HUnit Standard library unittest module

Examples

Hello world

Haskell

main = do
  print "enter your name"
  a <- getLine
  print $ "hello " ++ a

compile

ghc Main.hs
./Main 
# or runghc Main.hs

Nim

echo "enter your name"
let a = stdin.readLine()
echo "hello " & a

compile

nim c Main.nim
./Main
# or nim c --run Main.nim

case

Haskell

case expression

case (parse "<string>" number "45") of (Right x) -> x
                                       (Left err) -> print err >> fail "parse error"

pattern matching

fib :: Integer -> Integer
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
fibs::[Integer]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
main = print $ take 10 fibs

Nim

var 
  a = 0
  b = 0
  res: int
let s = "+"

case s:
of "+": res = a + b 
of "-": res = a - b
else: res = -1

Algebraic Data Type

Haskell

data Value = IVal Integer | FVal Double deriving (Show, Eq)

data Expr
  = Var String
  | Lit Value
  | App Expr Expr
  | Lam String Expr
  deriving (Eq, Show)

nim

type 
  ExprKind = enum
    Var, Lit, Lam, App
  Expr = ref object
    case kind: ExprKind
    of Var:
      name: string
    of Lit:
      val: float
    of App:
      a, b: Expr
    of Lam:
      n: string
      e: Expr

func eval(e: Expr): Expr = 
   case e.kind:
   of Var: ...

importing modules

{-# LANGUAGE Unsafe #-} -- since Unsafe.Coerce is unsafe, use LANGUAGE pragma to mark this module unsafe
module Main (hello) where -- expose hello function

import Unsafe.Coerce -- expose all symbols
import qualified Control.Monad.Writer as W -- W is the new name
import Data.ByteString hiding (putStrLn) -- readFile is not visible
import Data.Semigroup ((<>), Semigroup) -- import (<>) function and Semigroup typeclass

hello::String -- hello is type String, aka [Char]
{-# INLINABLE hello #-} -- tell ghc hello is inlineable
hello = "Hello "<>"world" -- the function body

main::IO () -- main is entry point of program, main is a empty tuple inside the IO Monad
main = putStrLn hello -- call putStrLn
{-
  {-
    multi line comment
  -}
-}

Nim

import System # expose all symbols
import System as S # S W is the new name
from System import create # import create
import System except int # import all symbols except int
import system as S except int # S is the new name, int is not imported

# hello is a procedure, return some string
proc hello(): string {.inline.} = "hello " & "world"

# three ways to call
# they are the same
echo hello()
hello().echo
echo(hello())

#[
  #[
       multi line comment
  ]#
]#
var mutable = "Win32"
mutable &= "API"

let immutable = ["Nim", "PlayGround", "C"]

const compileTime = 2 shl 1024

echo $mutable, $immutable, $compileTime

templates

Haskell

{-# LANGUAGE CPP #-}

#define bind(a, f) a <- f
#define COUNT 5

main::IO ()

main = do
  putStrLn "enter a number->"
  bind(s, getLine)
  bind(num, readIO s)::IO Integer
  print $ scanl (+) 0 (take COUNT $ repeat num)

nim

template curry(a, b, c) = 
  a (b, c)

curry(echo, 1, 2)

macros

Haskell

Macro.hs

{-# LANGUAGE TemplateHaskell #-}
module Macro
  ( duplicate )
where

import Language.Haskell.TH
import Language.Haskell.TH.Syntax

duplicate :: String -> Q Exp
duplicate s = do
  [| fromString s++s |]

Main.hs

{-# LANGUAGE TemplateHaskell #-}

module Main where

import Macro
main :: IO ()
main = putStrLn $(duplicate "<>")

Nim

import macros

macro readContent(path: static[string]): untyped = 
  let c = staticRead path
  result = newLit c

stdout.write readContent("Main.nim")

FFI

Haskell

module Main where
import Prelude hiding (sin)
import Foreign.C.Types

-- stdcall in windows
foreign import ccall "math.h sin"
    sin::CDouble->CDouble

main = print $ map sin (take 50 (scanl (+) 0 (repeat 0.001)))

Nim

proc sin(a: cdouble): cdouble {.importc, header: "math.h", nodecl.}

var i = 0.cdouble
while i < 0.05:
  echo sin(i)
  i += 0.001
Clone this wiki locally