Description
Write Emacs module in Haskell, using Emacs 25's Dynamic Module feature.
README.md
EXPERIMENTAL
Write Emacs module in Haskell, using Emacs 25's Dynamic Module feature.
- Only tested with linux.
- Only tested with Stack (LTS 6.26)
- You need to build emacs with --with-modules configuration options
- You need to specify some ghc-options to make it work
Sample:
{-# LANGUAGE ForeignFunctionInterface,OverloadedStrings #-}
module Main where
import Emacs
import Foreign.C.Types
foreign export ccall "emacs_module_init" emacsModuleInit :: EmacsModule
emacsModuleInit :: EmacsModule
emacsModuleInit = defmodule "sample-module" $ do
setVal "foo" (Symbol "bar")
defun "square" $ \i -> do
message "haskell squre function called"
return $ (i*i :: Int)
main :: IO ()
main = undefined
How to use
Explain using Stack and LTS 6.26 as premise.
1. Create a new project with Stack
$ stack --resolver=lts-6.26 new mymodule
2. Change executable name to *.so and add haskelisp to the dependency
mymodule.cabal:
executable mymodule.so
hs-source-dirs: app
main-is: Main.hs
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: base
, mymodule
, haskelisp
default-language: Haskell2010
3. Change ghc-options
and add cc-options
to make shared library
mymodule.cabal:
executable mymodule.so
...
cc-options: -fPIC
ghc-options: -shared -dynamic -fPIC -lHSrts-ghc7.10.3
...
4. Modules must be GPL compatible
The shared library must include plugin_is_GPL_compatible
symbol to be loaded by Emacs. Prepare a C source file and specify it with c-sources
option.
$ echo 'int plugin_is_GPL_compatible;' > plugin_is_GPL_compatible.c
mymodule.cabal:
executable mymodule.so
...
c-sources: plugin_is_GPL_compatible.c
...
5. Write some code
Main.hs:
{-# LANGUAGE ForeignFunctionInterface,OverloadedStrings #-}
module Main where
import Emacs
import Foreign.C.Types
foreign export ccall "emacs_module_init" emacsModuleInit :: EmacsModule
emacsModuleInit :: EmacsModule
emacsModuleInit = defmodule "mymodule" $ do
defun "mysquare" $ \i -> do
message "haskell squre function called"
return $ (i*i :: Int)
main :: IO ()
main = undefined
We don't need main
function, but without it cause a compile error, so include a dummy one. It won't be called.
6. Build
$ stack build
7. Copy the genereated shared library under load-path
For example, if ~/.emacs.d/lisp
is included in load-path
:
$ cp .stack-work/install/x86_64-linux/lts-6.26/7.10.3/bin/mymodule.so ~/.emacs.d/lisp/
8. Load your shared library
(require 'mymodule)