A Husk Quick-start with a Focus on the FFI to Haskell
Introduction ↟
This quick-start aims to get you up and running with Haskell, Husk Scheme, and the FFI for Husk. It will take you from 0 Kelvin to room temperature, putting within your grasp the heat of the sun's surface.
Below is a quick blurb on each of Haskell and Husk.
Haskell ↟
The Haskell programming language is a statically typed purely functional programming language with type inference, concurrency primitives, and lazy evaluation.
Key Haskell resources:
Husk Scheme ↟
From the Husk Scheme Github project:
Husk is a dialect of Scheme written in Haskell that implements a superset of the R5RS standard and a large portion of the R7RS-small language. Advanced features are provided including continuations, hygienic macros, libraries, and a full numeric tower.
One of the key features of Husk is its FFI to Haskell, allowing developers to take advantage of the Haskell ecosystem's libraries within their Husk projects.
Key Husk resources:
Dependencies ↟
- An operating system that can support Haskell.
git
- Stack
Setup & Build ↟
Once you have git
and stack
installed, you are ready to begin.
Dead-Simple ↟
The quickest way to build is with the following:
-
Clone the quick-start repo and
cd
into the clone directory:$ git clone https://github.com/haskell-lisp/husk-quick-start.git $ cd husk-quick-start
-
Then:
$ make
Under the Covers ↟
That make
target is actually performing the following.
-
Change to the Haskell project directory:
$ cd husk-quickstart
-
Get the project's supported version of Haskell:
$ stack setup
At which point you should see something like the following:
Preparing to install GHC to an isolated location. This will not interfere with any system-level installation. ghc-7.10.3: 113.16 MiB / 140.77 MiB ( 80.39%) downloaded...
-
At this point you are ready to build Husk and its dependencies:
$ stack build husk-scheme
-
And finally, any files we've created in our project (as well as project dependencies):
$ stack build
Note that, as long as husk-scheme is downloaded from a published source (i.e.,
declared in the extra-deps
section of the project stack.yaml
), you
won't need to explicitly call stack build husk-scheme
. However, since we're
using a version of Husk that's only available in the Github repo, we need to
make the extra stack
call.
Using Husk ↟
REPL ↟
To start the Husk REPL using the local install of Haskell and Husk, run the
following make
target:
$ make repl
Or use this stack
command (from inside the Haskell project directory):
$ stack exec huski
_ _ __ _
| | | | \\\ | |
| |__ _ _ ___| | __ \\\ ___ ___| |__ ___ _ __ ___ ___
| '_ \| | | / __| |/ / //\\\ / __|/ __| '_ \ / _ \ '_ ` _ \ / _ \
| | | | |_| \__ \ < /// \\\ \__ \ (__| | | | __/ | | | | | __/
|_| |_|\__,_|___/_|\_\ /// \\\ |___/\___|_| |_|\___|_| |_| |_|\___|
http://justinethier.github.io/husk-scheme
(c) 2010-2016 Justin Ethier
Version 3.19.2
huski>
Basics ↟
huski> (+ 2 15)
17
huski> (* 49 100)
4900
huski> (- 1892 1472)
420
huski> (/ 5 2)
5/2
huski> (/ 5.0 2)
2.5
huski> (= 1 1)
#t
huski> (< 1 1)
#f
huski> (<= 1 1)
#t
huski> (<= 2 1)
#f
huski> (<= 1 1)
#t
huski> (<= 1 2)
#t
huski> (<= 3 2)
#f
huski> (>= 3 2)
#t
huski> (not (= 1 1))
#f
Note that the same types must be compared:
huski> (= 5 #t)
Invalid type: expected number, found #t
Call History:
#0: (= 5 #t)
huski> (and #t #t)
#t
huski> (and #t #f)
#f
huski> (or #t #f)
#t
huski> (or #f #f)
#f
huski> (or #f #t)
#t
huski> (not (or #f #t))
#f
TBD
TBD
TBD
TBD
The Ackermann function is a well-known recursive function discovered by Wilhelm Ackermann (student of the famous mathematician David Hilbert). Go ahead and paste it into your REPL:
(define (ackermann m n)
(cond
((= m 0) (+ n 1))
((= n 0) (ackermann (- m 1) 1))
(else (ackermann
(- m 1)
(ackermann m (- n 1))))))
You can now call it:
huski> (ackermann 1 1)
3
huski> (ackermann 1 2)
4
huski> (ackermann 2 2)
7
huski> (ackermann 2 3)
9
huski> (ackermann 3 3)
61
huski> (ackermann 3 4)
125
Calling Haskell ↟
The following calls load Haskell functions which have been prepared for use in Husk:
huski> (load-ffi "Language.Scheme.Plugins.CPUTime" "precision" "cpu-time:precision")
<IO primitive>
huski> (load-ffi "Language.Scheme.Plugins.CPUTime" "get" "cpu-time:get")
<IO primitive>
It takes the arguments of module name, function in the module, Husk name by
which the function may be called. We can now use cpu-time:precision
and
cpu-time:get
in Husk:
(define (display-cpu-info)
(display "CPU time: ")
(write (cpu-time:get))
(display "CPU time precision: ")
(write (cpu-time:precision))
(display "Seconds of CPU time spent: ")
(display (exact->inexact (/ (cpu-time:get) 1000000000000)))
(newline))
Note that Haskell functions called from Husk must first be properly wrapped. The above two functions were prepared in the following manner:
module Language.Scheme.Plugins.CPUTime (get, precision) where
import Language.Scheme.Types
import System.CPUTime
import Control.Monad.Error
get, precision :: [LispVal] -> IOThrowsError LispVal
-- |Wrapper for CPUTime.getCPUTime
get [] = do
t <- liftIO $ System.CPUTime.getCPUTime
return $ Number t
get badArgList = throwError $ NumArgs 0 badArgList
-- |Wrapper for CPUTime.cpuTimePrecision
precision [] = return $ Number $ System.CPUTime.cpuTimePrecision
precision badArgList = throwError $ NumArgs 0 badArgList
Using Husk in a Project ↟
TBD
Calling Husk from Haskell ↟
TBD
License ↟
Copyright © 2016 Duncan McGreggor
Distributed under the Open Publication License