From 08f3c26aa20ae958bd889eeb60cf58a1f514a1b3 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Sun, 18 Feb 2024 01:43:30 +0530 Subject: [PATCH 01/31] introduction looks promising --- en/blog.md | 2 ++ en/haskell-rust-ffi.md | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 en/haskell-rust-ffi.md diff --git a/en/blog.md b/en/blog.md index 0b9c5034..5ec85d02 100644 --- a/en/blog.md +++ b/en/blog.md @@ -10,4 +10,6 @@ feed: >[!warning] WIP > We don't have any posts yet. Checkout our [[tutorial]] in the meanwhile. +- [[haskell-rust-ffi]]# + An RSS feed will be made available soon (cf. [this bug](https://github.com/srid/emanote/issues/490)). \ No newline at end of file diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md new file mode 100644 index 00000000..6caa74a5 --- /dev/null +++ b/en/haskell-rust-ffi.md @@ -0,0 +1,9 @@ +# How do you make using rust functions in haskell easier? + +Recently at work, there was a [[rust]] library that had a use case in most of the projects. The only problem being, we use a varietly of languages and re-rewriting the same code in all of them means: 1000s of developer hours down the drain. So, the maintainers decide to pull out the big guns: [FFI (Foreign Function Interface)](https://en.wikipedia.org/wiki/Foreign_function_interface). In simple words, languages decide to talk to each other by interoping with C (although not always, or atleast in the case of this blog post, yes). + +Everything is going well, the rust library outputs architecture specific binary files and the lanugage making the FFI call is able to do so by pointing to the binary files. Again, everything seems to be going well, like it did, until: + +- You need to run on someone else's machine, all that endless instructions to install rust, run `cargo build` and then, assuming you are doing FFI from [[haskell]], you need to install haskell build tools and run it, pffft, it's a nightmare. +- You write a library that does the FFI for you, then another package uses this library, only to realise that I can't run this without knowing where to find the rust binary files. + From 008237ab7b37c834fa1486a6b652562f23a9962a Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Wed, 27 Mar 2024 16:55:06 +0530 Subject: [PATCH 02/31] haskell-rust-ffi is no longer a blog but a tutorial Additionally change the introduction to be more tutorial-like and add the rust library section. Also, add placeholders for other sections. --- en/haskell-rust-ffi.md | 68 +++++++++++++++++++++++++++++++--- en/tutorial.md | 1 + global/haskell-rust-ffi/lib.rs | 9 +++++ 3 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 global/haskell-rust-ffi/lib.rs diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 6caa74a5..4070e350 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -1,9 +1,67 @@ -# How do you make using rust functions in haskell easier? +# Rust FFI in Haskell -Recently at work, there was a [[rust]] library that had a use case in most of the projects. The only problem being, we use a varietly of languages and re-rewriting the same code in all of them means: 1000s of developer hours down the drain. So, the maintainers decide to pull out the big guns: [FFI (Foreign Function Interface)](https://en.wikipedia.org/wiki/Foreign_function_interface). In simple words, languages decide to talk to each other by interoping with C (although not always, or atleast in the case of this blog post, yes). +This #[[tutorial|tutorial]] will show you how to use [[nix]] to simplify the workflow of using [[rust]] library as a dependency in your [[haskell]] project via [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface). -Everything is going well, the rust library outputs architecture specific binary files and the lanugage making the FFI call is able to do so by pointing to the binary files. Again, everything seems to be going well, like it did, until: +> [!info] Foreign Function Interface (FFI) +> This is not just limited between haskell and rust, it can be used between any two languages that can find a common ground to communicate with each other, in this case, C. -- You need to run on someone else's machine, all that endless instructions to install rust, run `cargo build` and then, assuming you are doing FFI from [[haskell]], you need to install haskell build tools and run it, pffft, it's a nightmare. -- You write a library that does the FFI for you, then another package uses this library, only to realise that I can't run this without knowing where to find the rust binary files. +The end goal of this tutorial is to be able to call a rust function that returns "Hello, from rust!" from a haskell package. Let's get started with the rust library. +## Initialize rust project + +Initialize a new rust project with [rust-nix-template](https://github.com/srid/rust-nix-template): + +```sh +git clone https://github.com/srid/rust-nix-template.git +cd rust-nix-template +``` + +Let's run the project: + +```sh +nix develop +just run +``` + +## Create a rust library + +The template we just initialized is a binary project, let's follow the convention for a library project and move the contents of `src/main.rs` to `src/lib.rs`: + +```sh +mv src/main.rs src/lib.rs +``` + +`cargo run` will no longer work, but we can still `cargo build`. + +As this is now a library, let's not worry about the arguments for it and just create a public function `hello` that returns a C-style string. Replace the contents of `src/lib.rs` with: + +[[haskell-rust-ffi/lib.rs]] +![[haskell-rust-ffi/lib.rs]] + +Read more about "Calling Rust code from C" [here](https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c). + +The library now builds, but we don't have the dynamic library yet. Let's add a `crate-type` to the `Cargo.toml`: + +```toml +[lib] +crate-type = ["cdylib"] +``` + +Now when you run `cargo build`, you should see a `librust_nix_template.dylib` (if you are on macOS) or `librust_nix_template.so` (if you are on Linux) in the `target/debug` directory. + +## Initialize haskell project + +TODO + +## Add rust library as a dependency + +TODO + +## Call rust function from haskell + +TODO + +## Template + +> [!warning] TODO +> Provide a link to the template and talk about the added benefits of formatting, lsp, etc. \ No newline at end of file diff --git a/en/tutorial.md b/en/tutorial.md index cabd918f..48bc4a0b 100644 --- a/en/tutorial.md +++ b/en/tutorial.md @@ -14,6 +14,7 @@ order: -100 - [[hm-tutorial]]# - [[dev]] tutorial series - [[nixify-haskell]] + - [[haskell-rust-ffi]] - [ ] CI/CD tutorial series diff --git a/global/haskell-rust-ffi/lib.rs b/global/haskell-rust-ffi/lib.rs new file mode 100644 index 00000000..eae48782 --- /dev/null +++ b/global/haskell-rust-ffi/lib.rs @@ -0,0 +1,9 @@ +use std::ffi::CString; +use std::os::raw::c_char; + +/// A function that returns "Hello, from rust!" as a C style string. +#[no_mangle] +pub extern "C" fn hello() -> *mut c_char { + let s = CString::new("Hello, from rust!").unwrap(); + s.into_raw() +} \ No newline at end of file From b7c82313cd9b246eccd6979d53b369c7e32a599a Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Wed, 27 Mar 2024 17:04:46 +0530 Subject: [PATCH 03/31] moving main.rs to lib.rs is not the convention --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 4070e350..c926dc13 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -25,7 +25,7 @@ just run ## Create a rust library -The template we just initialized is a binary project, let's follow the convention for a library project and move the contents of `src/main.rs` to `src/lib.rs`: +The template we just initialized is a binary project, let's follow the convention for a library project and use `lib.rs`: ```sh mv src/main.rs src/lib.rs From e136dbadbe8f89a1c009a51861ba8abb2a87beaa Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Wed, 27 Mar 2024 17:07:54 +0530 Subject: [PATCH 04/31] what are the dynamic library files for? --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index c926dc13..24cdc6e5 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -40,7 +40,7 @@ As this is now a library, let's not worry about the arguments for it and just cr Read more about "Calling Rust code from C" [here](https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c). -The library now builds, but we don't have the dynamic library yet. Let's add a `crate-type` to the `Cargo.toml`: +The library now builds, but we don't have the dynamic library files that are required for FFI. For this, let's add a `crate-type` to the `Cargo.toml`: ```toml [lib] From 1f95b334f908eae91eef62f0f7408fe834e923e4 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Wed, 27 Mar 2024 17:09:53 +0530 Subject: [PATCH 05/31] add heading anchors --- en/haskell-rust-ffi.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 24cdc6e5..2da3d15f 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -7,6 +7,7 @@ This #[[tutorial|tutorial]] will show you how to use [[nix]] to simplify the wor The end goal of this tutorial is to be able to call a rust function that returns "Hello, from rust!" from a haskell package. Let's get started with the rust library. +{#init-rust} ## Initialize rust project Initialize a new rust project with [rust-nix-template](https://github.com/srid/rust-nix-template): @@ -23,6 +24,7 @@ nix develop just run ``` +{#rust-lib} ## Create a rust library The template we just initialized is a binary project, let's follow the convention for a library project and use `lib.rs`: @@ -49,6 +51,7 @@ crate-type = ["cdylib"] Now when you run `cargo build`, you should see a `librust_nix_template.dylib` (if you are on macOS) or `librust_nix_template.so` (if you are on Linux) in the `target/debug` directory. +{#init-haskell} ## Initialize haskell project TODO From f016a998ffc40f39eeae31c4a64af2f29d1e4f7d Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:36:36 +0530 Subject: [PATCH 06/31] better intro --- en/haskell-rust-ffi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 2da3d15f..89fa33d6 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -1,11 +1,11 @@ # Rust FFI in Haskell -This #[[tutorial|tutorial]] will show you how to use [[nix]] to simplify the workflow of using [[rust]] library as a dependency in your [[haskell]] project via [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface). +This #[[tutorial|tutorial]] will show you how to use [[nix]] to simplify the workflow of using [[rust]] library as a dependency in your [[haskell]] project via [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface). If you are new to [[nix]], first go through the [[nix-tutorial]]. > [!info] Foreign Function Interface (FFI) -> This is not just limited between haskell and rust, it can be used between any two languages that can find a common ground to communicate with each other, in this case, C. +> This is not just limited to haskell and rust, it can be used between any two languages that can find a common ground to communicate with each other, in this case, C. -The end goal of this tutorial is to be able to call a rust function that returns "Hello, from rust!" from a haskell package. Let's get started with the rust library. +The end goal of this tutorial is to be able to call a rust function that returns `Hello, from rust!` from a haskell package. Let's get started with the rust library. {#init-rust} ## Initialize rust project From 972301f26f80737d4b565664da893878157cbe0f Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:37:01 +0530 Subject: [PATCH 07/31] minify "create rust library" section --- en/haskell-rust-ffi.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 89fa33d6..5e3f56dc 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -27,15 +27,7 @@ just run {#rust-lib} ## Create a rust library -The template we just initialized is a binary project, let's follow the convention for a library project and use `lib.rs`: - -```sh -mv src/main.rs src/lib.rs -``` - -`cargo run` will no longer work, but we can still `cargo build`. - -As this is now a library, let's not worry about the arguments for it and just create a public function `hello` that returns a C-style string. Replace the contents of `src/lib.rs` with: +The template we just initialized is a binary project, we will need a library project. The library must export a function that we can call from haskell, for simplicity, let's export a function `hello` that returns a `C-style string`. Create a new file `src/lib.rs` with contents: [[haskell-rust-ffi/lib.rs]] ![[haskell-rust-ffi/lib.rs]] From a8fbd3d50f8efe120b79c739e484558ce361a8b7 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:37:22 +0530 Subject: [PATCH 08/31] bold rust FFI doc --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 5e3f56dc..9be60e61 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -32,7 +32,7 @@ The template we just initialized is a binary project, we will need a library pro [[haskell-rust-ffi/lib.rs]] ![[haskell-rust-ffi/lib.rs]] -Read more about "Calling Rust code from C" [here](https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c). +Read more about **Calling Rust code from C** [here](https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c). The library now builds, but we don't have the dynamic library files that are required for FFI. For this, let's add a `crate-type` to the `Cargo.toml`: From 961314084354a7e8d740885c6a17efd0df5f7411 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:37:39 +0530 Subject: [PATCH 09/31] init haskell project --- en/haskell-rust-ffi.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 9be60e61..55ba44f1 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -46,7 +46,25 @@ Now when you run `cargo build`, you should see a `librust_nix_template.dylib` (i {#init-haskell} ## Initialize haskell project -TODO +Temporarily add to the `devShells.default` in `flake.nix`: + +```nix +{ + # Inside devShells.default + nativeBuildInputs = with pkgs; [ + # ... + cabal-install + ghc + ]; +} + +``` + +Then, run: + +```sh +nix develop -c cabal init -n --exe -m --simple hello-haskell +``` ## Add rust library as a dependency From 66fc6e555e32f74015fa6e149a0dfc48cbab8557 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:37:55 +0530 Subject: [PATCH 10/31] nixify haskell project --- en/haskell-rust-ffi.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 55ba44f1..cba4b328 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -66,6 +66,38 @@ Then, run: nix develop -c cabal init -n --exe -m --simple hello-haskell ``` +{#nixify-haskell} +## Nixify haskell project + +We will use [haskell-flake](https://community.flake.parts/haskell-flake) to nixify the haskell project. Add the following to `./hello-haskell/default.nix`: + +[[haskell-rust-ffi/hs/default.nix]] +![[haskell-rust-ffi/hs/default.nix]] + +Additionally, add the following to `flake.nix`: + +```nix +{ + inputs.haskell-flake.url = "github:srid/haskell-flake"; + + outputs = inputs: + # Inside `mkFlake` + { + imports = [ + ./hello-haskell + ]; + }; +} +``` + +Stage the changes: + +```sh +git add hello-haskell +``` + +Now, `nix run .#hello-haskell` to build and run the haskell project. + ## Add rust library as a dependency TODO From 2b2126097b05d97573ab56126fb8fe17ee0d9450 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:38:05 +0530 Subject: [PATCH 11/31] merge devshells --- en/haskell-rust-ffi.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index cba4b328..63ec2909 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -98,6 +98,32 @@ git add hello-haskell Now, `nix run .#hello-haskell` to build and run the haskell project. +{#merge-devshell} +## Merge Rust and Haskell development environments + +We created `devShells.haskell` in the previous section. Let's merge it with the Rust development environment in `flake.nix`: + +```nix +{ + # Inside devShells.default + inputsFrom = [ + # ... + self'.devShells.haskell + ]; +} +``` + +Re-enter the shell and you now have both Rust and Haskell development environments: + +```sh +exit +nix develop +cd hello-haskell && cabal build +cd .. && cargo build +``` + +{#add-rust-lib} + ## Add rust library as a dependency TODO From 6cb26b6a0f7cb097f14dffe831a53fc5934ee7cf Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:38:29 +0530 Subject: [PATCH 12/31] add rust library as dep to haskell pkg --- en/haskell-rust-ffi.md | 49 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 63ec2909..3f9bfbdf 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -126,7 +126,54 @@ cd .. && cargo build ## Add rust library as a dependency -TODO +As any other depedency, you will first add them to your `.cabal` file: + +```cabal +executable hello-haskell + -- ... + extra-libraries: rust_nix_template +``` + +Try to build it: + +```sh +cd hello-haskell && cabal build +``` + +And you will see an error like this: + +```sh +... +* Missing (or bad) C library: rust_nix_template +... +``` + +The easiest thing to do would be to `export LIBRARY_PATH=../target/debug`. This would mean that building the rust project will always be an extra command you run in the setup/distribution process. And it only gets harder when you have more dependencies and are spread across repositories. + +Even on trying to re-enter the `devShell`, the haskell package derivation will not resolve as it can't find `rust_nix_template`: + +```sh +... +error: function 'anonymous lambda' called without required argument 'rust_nix_template' +... +``` + +Several times, easiest thing to do is not the simplest, let's use nix to simplify this process. + +Edit `hello-haskell/default.nix` to: + +```nix +{ + # Inside haskellProjects.default + otherOverlays = [ + (_: _: { + rust_nix_template = self'.packages.default; + }) + ]; +} +``` + +This will not require user to manually build the rust project because we have autowired it as a pre-requisite to the haskell package. ## Call rust function from haskell From 2d3baac340228d9374c3e03bc0a20f2579fdf9cf Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:38:41 +0530 Subject: [PATCH 13/31] call rust code from haskell --- en/haskell-rust-ffi.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 3f9bfbdf..e2a16db7 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -177,7 +177,32 @@ This will not require user to manually build the rust project because we have au ## Call rust function from haskell -TODO +Replace the contents of `hello-haskell/app/Main.hs` with: + +[[haskell-rust-ffi/hs/Main.hs]] +![[haskell-rust-ffi/hs/Main.hs]] + +The implementation above is based on the [Haskell FFI documentation](https://wiki.haskell.org/Foreign_Function_Interface). Now, run the haskell project: + +```sh +nix run .#hello-haskell +``` + +You should see the output `Hello, from rust!`. + +> [!note] MacOS caveat +> If you are on MacOS, the haskell package will not run because `dlopen` will be looking for the `.dylib` file in the temporary build directory (`/private/tmp/nix-build-rust-nix...`). To fix this, you will need [fixDarwinDylibNames](https://github.com/NixOS/nixpkgs/blob/af8fd52e05c81eafcfd4fb9fe7d3553b61472712/pkgs/build-support/setup-hooks/fix-darwin-dylib-names.sh) in `flake.nix`: +> +>```nix +>{ +> # Inside `perSystem.packages.default` +> # ... +> buildInputs = if pkgs.stdenv.isDarwin then [ pkgs.fixDarwinDylibNames ] else [ ]; +> postInstall = '' +> ${if pkgs.stdenv.isDarwin then "fixDarwinDylibNames" else ""} +> ''; +>} +>``` ## Template From 50b1fcf5bc3eb5ba1a827e7af6e12d40f9f311ea Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:38:55 +0530 Subject: [PATCH 14/31] problems with cabal repl --- en/haskell-rust-ffi.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index e2a16db7..27eca258 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -204,6 +204,37 @@ You should see the output `Hello, from rust!`. >} >``` +## Problems with `cabal repl` + +`cabal repl` doesn't look for `NIX_LDFLAGS` to find the dynamic library, see why [here](https://discourse.nixos.org/t/shared-libraries-error-with-cabal-repl-in-nix-shell/8921/10). This can be worked around in `hello-haskell/default.nix` using: + +```nix +{ + # Inside `devShells.haskell` + shellHook = '' + export LIBRARY_PATH=${config.haskellProjects.default.outputs.finalPackages.rust_nix_template}/lib + ''; +} +``` + +Re-enter the shell, and voila! + +```sh +❯ cd hello-haskell && cabal repl +Build profile: -w ghc-9.4.8 -O1 +In order, the following will be built (use -v for more details): + - hello-haskell-0.1.0.0 (exe:hello-haskell) (ephemeral targets) +Preprocessing executable 'hello-haskell' for hello-haskell-0.1.0.0.. +GHCi, version 9.4.8: https://www.haskell.org/ghc/ :? for help +[1 of 2] Compiling Main ( app/Main.hs, interpreted ) +Ok, one module loaded. +ghci> main +Hello, from rust! +``` + +> [!note] What about `ghci`? +> If you use `ghci` you will have to link the library manually: `ghci -lrust_nix_template`. See the [documentation](https://downloads.haskell.org/ghc/latest/docs/users_guide/ghci.html#extra-libraries). + ## Template > [!warning] TODO From 5274f85a4891e4d0f46bd89e22e7c98fc33da3af Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:39:05 +0530 Subject: [PATCH 15/31] add template --- en/haskell-rust-ffi.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 27eca258..d4fa894e 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -237,5 +237,4 @@ Hello, from rust! ## Template -> [!warning] TODO -> Provide a link to the template and talk about the added benefits of formatting, lsp, etc. \ No newline at end of file +You can find the template at . This template additionally comes with formatting setup with [[treefmt|treefmt-nix]] and VSCode integration. From 3b71f94b3e5da95427940ed7c1be196cd73e6482 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:39:27 +0530 Subject: [PATCH 16/31] include the global files --- global/haskell-rust-ffi/hs/Main.hs | 17 +++++++++++++++++ global/haskell-rust-ffi/hs/default.nix | 15 +++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 global/haskell-rust-ffi/hs/Main.hs create mode 100644 global/haskell-rust-ffi/hs/default.nix diff --git a/global/haskell-rust-ffi/hs/Main.hs b/global/haskell-rust-ffi/hs/Main.hs new file mode 100644 index 00000000..a9c85697 --- /dev/null +++ b/global/haskell-rust-ffi/hs/Main.hs @@ -0,0 +1,17 @@ +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} + +{-# HLINT ignore "Use camelCase" #-} + +module Main where + +import Foreign.C.String (CString, peekCString) + +-- | The `hello` function exported by the `rust_nix_template` library. +foreign import ccall "hello" hello_rust :: IO CString + +-- | Call `hello_rust` and convert the result to a Haskell `String`. +hello_haskell :: IO String +hello_haskell = hello_rust >>= peekCString + +main :: IO () +main = hello_haskell >>= putStrLn diff --git a/global/haskell-rust-ffi/hs/default.nix b/global/haskell-rust-ffi/hs/default.nix new file mode 100644 index 00000000..1f2252a6 --- /dev/null +++ b/global/haskell-rust-ffi/hs/default.nix @@ -0,0 +1,15 @@ +{ + perSystem = { config, pkgs, self', ... }: { + haskellProjects.default = { + projectRoot = ./.; + autoWire = [ "packages" "checks" "apps" ]; + }; + + devShells.haskell = pkgs.mkShell { + name = "hello-haskell"; + inputsFrom = [ + config.haskellProjects.default.outputs.devShell + ]; + }; + }; +} From 01342afce12d032fe11556b53aa3aa9602af7fd0 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 28 Mar 2024 16:40:30 +0530 Subject: [PATCH 17/31] add remaining heading anchors --- en/haskell-rust-ffi.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index d4fa894e..89e9fe43 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -123,7 +123,6 @@ cd .. && cargo build ``` {#add-rust-lib} - ## Add rust library as a dependency As any other depedency, you will first add them to your `.cabal` file: @@ -175,6 +174,7 @@ Edit `hello-haskell/default.nix` to: This will not require user to manually build the rust project because we have autowired it as a pre-requisite to the haskell package. +{#call-rust} ## Call rust function from haskell Replace the contents of `hello-haskell/app/Main.hs` with: @@ -204,6 +204,7 @@ You should see the output `Hello, from rust!`. >} >``` +{#cabal-repl} ## Problems with `cabal repl` `cabal repl` doesn't look for `NIX_LDFLAGS` to find the dynamic library, see why [here](https://discourse.nixos.org/t/shared-libraries-error-with-cabal-repl-in-nix-shell/8921/10). This can be worked around in `hello-haskell/default.nix` using: @@ -235,6 +236,7 @@ Hello, from rust! > [!note] What about `ghci`? > If you use `ghci` you will have to link the library manually: `ghci -lrust_nix_template`. See the [documentation](https://downloads.haskell.org/ghc/latest/docs/users_guide/ghci.html#extra-libraries). +{#tpl} ## Template You can find the template at . This template additionally comes with formatting setup with [[treefmt|treefmt-nix]] and VSCode integration. From 04a406a636b7f90f3e81cef8f7716768bc175294 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 29 Mar 2024 10:47:48 +0530 Subject: [PATCH 18/31] `custom` settings problem with new pkgs is now fixed in haskell-flake see: https://github.com/srid/haskell-flake/pull/284 --- en/haskell-rust-ffi.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 89e9fe43..21798742 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -164,11 +164,9 @@ Edit `hello-haskell/default.nix` to: ```nix { # Inside haskellProjects.default - otherOverlays = [ - (_: _: { - rust_nix_template = self'.packages.default; - }) - ]; + settings = { + rust_nix_template.custom = _: self'.packages.default; + }; } ``` From 00a2bf2a5266223da7b28d2f8abc938209cae454 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Mon, 1 Apr 2024 07:31:20 +0530 Subject: [PATCH 19/31] hyphens disallowed in library names --- en/haskell-rust-ffi.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 21798742..4193155c 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -41,7 +41,9 @@ The library now builds, but we don't have the dynamic library files that are req crate-type = ["cdylib"] ``` -Now when you run `cargo build`, you should see a `librust_nix_template.dylib` (if you are on macOS) or `librust_nix_template.so` (if you are on Linux) in the `target/debug` directory. +Now when you run `cargo build`, you should see a `librust_nix_template.dylib`[^hyphens-disallowed] (if you are on macOS) or `librust_nix_template.so` (if you are on Linux) in the `target/debug` directory. + +[^hyphens-disallowed]: Hyphens are not allowed in the library name, hence `librust_nix_template.dylib`. Explicitly setting the name of the library with hyphens will fail while parsing the manifest with: `library target names cannot contain hyphens: rust-nix-template` {#init-haskell} ## Initialize haskell project From d5b1410cabd32244221c9083243997c6cd35c3e2 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Mon, 1 Apr 2024 07:31:47 +0530 Subject: [PATCH 20/31] simpler haskell pkg init --- en/haskell-rust-ffi.md | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 4193155c..a15ed3fe 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -48,24 +48,14 @@ Now when you run `cargo build`, you should see a `librust_nix_template.dylib`[^h {#init-haskell} ## Initialize haskell project -Temporarily add to the `devShells.default` in `flake.nix`: +Fetch `cabal-install` and `ghc` from the `nixpkgs` input of current flake and initialize a new haskell project[^why-proj-nixpkgs]: -```nix -{ - # Inside devShells.default - nativeBuildInputs = with pkgs; [ - # ... - cabal-install - ghc - ]; -} - -``` - -Then, run: +[^why-proj-nixpkgs]: `cabal init` sets up the `base` package constraints based on GHC version, by using the `nixpkgs` from current flake, we ensure reproducibility. ```sh -nix develop -c cabal init -n --exe -m --simple hello-haskell +nixpkgs_url="github:nixos/nixpkgs/$(nix flake metadata --json | nix run nixpkgs#jq -- '.locks.nodes.nixpkgs.locked.rev' -r)" && \ +nix shell $nixpkgs_url#cabal-install $nixpkgs_url#ghc -c \ +cabal init -n --exe -m --simple hello-haskell ``` {#nixify-haskell} From 57ca2b0aa14c87a95393e06ed8e5bb115a113b54 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Mon, 1 Apr 2024 07:32:04 +0530 Subject: [PATCH 21/31] flakes is a pre-requisite --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index a15ed3fe..5a45ebda 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -1,6 +1,6 @@ # Rust FFI in Haskell -This #[[tutorial|tutorial]] will show you how to use [[nix]] to simplify the workflow of using [[rust]] library as a dependency in your [[haskell]] project via [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface). If you are new to [[nix]], first go through the [[nix-tutorial]]. +This #[[tutorial|tutorial]] will show you how to use [[nix]] to simplify the workflow of using [[rust]] library as a dependency in your [[haskell]] project via [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface). If you are new to [[nix]] and [[flakes]], first go through the [[nix-tutorial]]. > [!info] Foreign Function Interface (FFI) > This is not just limited to haskell and rust, it can be used between any two languages that can find a common ground to communicate with each other, in this case, C. From 0e654f1eda9c786e71f82790c51d154fad669553 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Mon, 1 Apr 2024 08:06:59 +0530 Subject: [PATCH 22/31] grammar --- en/haskell-rust-ffi.md | 76 ++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 5a45ebda..543b6552 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -1,23 +1,23 @@ # Rust FFI in Haskell -This #[[tutorial|tutorial]] will show you how to use [[nix]] to simplify the workflow of using [[rust]] library as a dependency in your [[haskell]] project via [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface). If you are new to [[nix]] and [[flakes]], first go through the [[nix-tutorial]]. +This #[[tutorial|tutorial]] will guide you through using [[nix]] to simplify the workflow of incorporating [[rust]] library as a dependency in your [[haskell]] project via [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface). If you are new to [[nix]] and [[flakes]], I recommend starting with the [[nix-tutorial]]. > [!info] Foreign Function Interface (FFI) -> This is not just limited to haskell and rust, it can be used between any two languages that can find a common ground to communicate with each other, in this case, C. +> This isn't solely restricted to Haskell and Rust, it can be used between any two languages that can establish a common ground to communicate, such as C. -The end goal of this tutorial is to be able to call a rust function that returns `Hello, from rust!` from a haskell package. Let's get started with the rust library. +The objective of this tutorial is to demonstrate calling a Rust function that returns `Hello, from rust!` from within a Haskell package. Let's begin by setting up the Rust library. {#init-rust} -## Initialize rust project +## Initialize Rust Project -Initialize a new rust project with [rust-nix-template](https://github.com/srid/rust-nix-template): +Start by initializing a new Rust project using [rust-nix-template](https://github.com/srid/rust-nix-template): ```sh git clone https://github.com/srid/rust-nix-template.git cd rust-nix-template ``` -Let's run the project: +Now, let's run the project: ```sh nix develop @@ -25,32 +25,34 @@ just run ``` {#rust-lib} -## Create a rust library +## Create a Rust Library -The template we just initialized is a binary project, we will need a library project. The library must export a function that we can call from haskell, for simplicity, let's export a function `hello` that returns a `C-style string`. Create a new file `src/lib.rs` with contents: +The template we've initialized is a binary project, but we need a library project. The library should export a function callable from Haskell. For simplicity, let's export a function named `hello` that returns a `C-style string`. + +Create a new file named `src/lib.rs` with the following contents: [[haskell-rust-ffi/lib.rs]] ![[haskell-rust-ffi/lib.rs]] -Read more about **Calling Rust code from C** [here](https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c). +You can learn more about **Calling Rust code from C** [here](https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c). -The library now builds, but we don't have the dynamic library files that are required for FFI. For this, let's add a `crate-type` to the `Cargo.toml`: +Now, the library builds, but we need the dynamic library files required for FFI. To achieve this, let's add a `crate-type` to the `Cargo.toml`: ```toml [lib] crate-type = ["cdylib"] ``` -Now when you run `cargo build`, you should see a `librust_nix_template.dylib`[^hyphens-disallowed] (if you are on macOS) or `librust_nix_template.so` (if you are on Linux) in the `target/debug` directory. +After running `cargo build`, you should find a `librust_nix_template.dylib`[^hyphens-disallowed] (if you are on macOS) or `librust_nix_template.so` (if you are on Linux) in the `target/debug` directory. -[^hyphens-disallowed]: Hyphens are not allowed in the library name, hence `librust_nix_template.dylib`. Explicitly setting the name of the library with hyphens will fail while parsing the manifest with: `library target names cannot contain hyphens: rust-nix-template` +[^hyphens-disallowed]: Note that the hyphens are disallowed in the library name; hence it's named `librust_nix_template.dylib`. Explicitly setting the name of the library with hyphens will fail while parsing the manifest with: `library target names cannot contain hyphens: rust-nix-template` {#init-haskell} -## Initialize haskell project +## Initialize Haskell Project -Fetch `cabal-install` and `ghc` from the `nixpkgs` input of current flake and initialize a new haskell project[^why-proj-nixpkgs]: +Fetch `cabal-install` and `ghc` from the `nixpkgs` input of current flake and initialize a new Haskell project[^why-proj-nixpkgs]: -[^why-proj-nixpkgs]: `cabal init` sets up the `base` package constraints based on GHC version, by using the `nixpkgs` from current flake, we ensure reproducibility. +[^why-proj-nixpkgs]: This approach ensures reproducibility as `cabal init` uses the version of GHC to initialize the `base` package constraints. ```sh nixpkgs_url="github:nixos/nixpkgs/$(nix flake metadata --json | nix run nixpkgs#jq -- '.locks.nodes.nixpkgs.locked.rev' -r)" && \ @@ -59,9 +61,9 @@ cabal init -n --exe -m --simple hello-haskell ``` {#nixify-haskell} -## Nixify haskell project +## Nixify Haskell Project -We will use [haskell-flake](https://community.flake.parts/haskell-flake) to nixify the haskell project. Add the following to `./hello-haskell/default.nix`: +We will utilize [haskell-flake](https://community.flake.parts/haskell-flake) to nixify the Haskell project. Add the following to `./hello-haskell/default.nix`: [[haskell-rust-ffi/hs/default.nix]] ![[haskell-rust-ffi/hs/default.nix]] @@ -88,12 +90,12 @@ Stage the changes: git add hello-haskell ``` -Now, `nix run .#hello-haskell` to build and run the haskell project. +Now, you can run `nix run .#hello-haskell` to build and execute the Haskell project. {#merge-devshell} -## Merge Rust and Haskell development environments +## Merge Rust and Haskell Development Environments -We created `devShells.haskell` in the previous section. Let's merge it with the Rust development environment in `flake.nix`: +In the previous section, we created `devShells.haskell`. Let's merge it with the Rust development environment in `flake.nix`: ```nix { @@ -105,7 +107,7 @@ We created `devShells.haskell` in the previous section. Let's merge it with the } ``` -Re-enter the shell and you now have both Rust and Haskell development environments: +Now, re-enter the shell, and you'll have both Rust and Haskell development environments: ```sh exit @@ -115,9 +117,9 @@ cd .. && cargo build ``` {#add-rust-lib} -## Add rust library as a dependency +## Add Rust Library as a Dependency -As any other depedency, you will first add them to your `.cabal` file: +Just like any other dependency, you'll first add it to your `.cabal` file: ```cabal executable hello-haskell @@ -125,13 +127,13 @@ executable hello-haskell extra-libraries: rust_nix_template ``` -Try to build it: +Try building it: ```sh cd hello-haskell && cabal build ``` -And you will see an error like this: +You'll likely encounter an error like this: ```sh ... @@ -139,9 +141,11 @@ And you will see an error like this: ... ``` -The easiest thing to do would be to `export LIBRARY_PATH=../target/debug`. This would mean that building the rust project will always be an extra command you run in the setup/distribution process. And it only gets harder when you have more dependencies and are spread across repositories. +The easiest solution might seem to be `export LIBRARY_PATH=../target/debug`. However, this would mean additional command for building the Rust project each time during setup or distribution. This process becomes even more complex with multiple dependencies spread across repositories. + +Often, the easiest solution isn't the simplest. Let's use Nix to simplify this process. -Even on trying to re-enter the `devShell`, the haskell package derivation will not resolve as it can't find `rust_nix_template`: +Even after re-entering the `devShell`, the Haskell package derivation won't resolve because it can't find `rust_nix_template`: ```sh ... @@ -149,9 +153,7 @@ error: function 'anonymous lambda' called without required argument 'rust_nix_te ... ``` -Several times, easiest thing to do is not the simplest, let's use nix to simplify this process. - -Edit `hello-haskell/default.nix` to: +To fix that, let's edit `hello-haskell/default.nix` to: ```nix { @@ -162,17 +164,17 @@ Edit `hello-haskell/default.nix` to: } ``` -This will not require user to manually build the rust project because we have autowired it as a pre-requisite to the haskell package. +This eliminates the need for manual Rust project building as it's wired as a prerequisite to the Haskell package. {#call-rust} -## Call rust function from haskell +## Call Rust function from Haskell Replace the contents of `hello-haskell/app/Main.hs` with: [[haskell-rust-ffi/hs/Main.hs]] ![[haskell-rust-ffi/hs/Main.hs]] -The implementation above is based on the [Haskell FFI documentation](https://wiki.haskell.org/Foreign_Function_Interface). Now, run the haskell project: +The implementation above is based on the [Haskell FFI documentation](https://wiki.haskell.org/Foreign_Function_Interface). Now, run the Haskell project: ```sh nix run .#hello-haskell @@ -181,7 +183,7 @@ nix run .#hello-haskell You should see the output `Hello, from rust!`. > [!note] MacOS caveat -> If you are on MacOS, the haskell package will not run because `dlopen` will be looking for the `.dylib` file in the temporary build directory (`/private/tmp/nix-build-rust-nix...`). To fix this, you will need [fixDarwinDylibNames](https://github.com/NixOS/nixpkgs/blob/af8fd52e05c81eafcfd4fb9fe7d3553b61472712/pkgs/build-support/setup-hooks/fix-darwin-dylib-names.sh) in `flake.nix`: +> If you are on MacOS, the Haskell package will not run because `dlopen` will be looking for the `.dylib` file in the temporary build directory (`/private/tmp/nix-build-rust-nix...`). To fix this, you need to include [fixDarwinDylibNames](https://github.com/NixOS/nixpkgs/blob/af8fd52e05c81eafcfd4fb9fe7d3553b61472712/pkgs/build-support/setup-hooks/fix-darwin-dylib-names.sh) in `flake.nix`: > >```nix >{ @@ -208,7 +210,7 @@ You should see the output `Hello, from rust!`. } ``` -Re-enter the shell, and voila! +Re-enter the shell, and you're set: ```sh ❯ cd hello-haskell && cabal repl @@ -224,9 +226,9 @@ Hello, from rust! ``` > [!note] What about `ghci`? -> If you use `ghci` you will have to link the library manually: `ghci -lrust_nix_template`. See the [documentation](https://downloads.haskell.org/ghc/latest/docs/users_guide/ghci.html#extra-libraries). +> If you use `ghci` you will need to link the library manually: `ghci -lrust_nix_template`. See the [documentation](https://downloads.haskell.org/ghc/latest/docs/users_guide/ghci.html#extra-libraries). {#tpl} ## Template -You can find the template at . This template additionally comes with formatting setup with [[treefmt|treefmt-nix]] and VSCode integration. +You can find the template at . This template also includes formatting setup with [[treefmt|treefmt-nix]] and VSCode integration. From 53182ba9458dcd57f822851342dc1a8724c6d41d Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Mon, 1 Apr 2024 08:24:32 +0530 Subject: [PATCH 23/31] more grammar --- en/haskell-rust-ffi.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 543b6552..8bf656d3 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -27,14 +27,13 @@ just run {#rust-lib} ## Create a Rust Library -The template we've initialized is a binary project, but we need a library project. The library should export a function callable from Haskell. For simplicity, let's export a function named `hello` that returns a `C-style string`. - -Create a new file named `src/lib.rs` with the following contents: +The template we've initialized is a binary project, but we need a library project. The library should export a function callable from Haskell. For simplicity, let's export a function named `hello` that returns a `C-style string`. To do so, create a new file named `src/lib.rs` with the following contents: [[haskell-rust-ffi/lib.rs]] ![[haskell-rust-ffi/lib.rs]] -You can learn more about **Calling Rust code from C** [here](https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c). +> [!info] Calling Rust code from C +> You can learn more about it [here](https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c). Now, the library builds, but we need the dynamic library files required for FFI. To achieve this, let's add a `crate-type` to the `Cargo.toml`: @@ -141,11 +140,11 @@ You'll likely encounter an error like this: ... ``` -The easiest solution might seem to be `export LIBRARY_PATH=../target/debug`. However, this would mean additional command for building the Rust project each time during setup or distribution. This process becomes even more complex with multiple dependencies spread across repositories. +The easiest solution might seem to be `export LIBRARY_PATH=../target/debug`. However, this is not reproducible and would mean running an additional command to setup the prerequisite to build the Haskell package. Even worse if the rust project is in a different repository. Often, the easiest solution isn't the simplest. Let's use Nix to simplify this process. -Even after re-entering the `devShell`, the Haskell package derivation won't resolve because it can't find `rust_nix_template`: +When you use Nix, you set up all the prerequisites beforehand, which is why you'll encounter an error when trying to re-enter the devShell without explicitly specifying where the Rust project is: ```sh ... @@ -153,7 +152,7 @@ error: function 'anonymous lambda' called without required argument 'rust_nix_te ... ``` -To fix that, let's edit `hello-haskell/default.nix` to: +To specify the Rust project as a dependency, let's edit `hello-haskell/default.nix` to: ```nix { @@ -164,7 +163,7 @@ To fix that, let's edit `hello-haskell/default.nix` to: } ``` -This eliminates the need for manual Rust project building as it's wired as a prerequisite to the Haskell package. +This process eliminates the need for manual Rust project building as it's wired as a prerequisite to the Haskell package. {#call-rust} ## Call Rust function from Haskell From 7112a0a67b3d39ab87b706c42156d8d2d4825c14 Mon Sep 17 00:00:00 2001 From: Shivaraj B H Date: Tue, 2 Apr 2024 12:01:42 +0530 Subject: [PATCH 24/31] we, not I Co-authored-by: Sridhar Ratnakumar <3998+srid@users.noreply.github.com> --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 8bf656d3..264308b7 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -1,6 +1,6 @@ # Rust FFI in Haskell -This #[[tutorial|tutorial]] will guide you through using [[nix]] to simplify the workflow of incorporating [[rust]] library as a dependency in your [[haskell]] project via [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface). If you are new to [[nix]] and [[flakes]], I recommend starting with the [[nix-tutorial]]. +This #[[tutorial|tutorial]] will guide you through using [[nix]] to simplify the workflow of incorporating [[rust]] library as a dependency in your [[haskell]] project via [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface). If you are new to [[nix]] and [[flakes]], we recommend starting with the [[nix-tutorial]]. > [!info] Foreign Function Interface (FFI) > This isn't solely restricted to Haskell and Rust, it can be used between any two languages that can establish a common ground to communicate, such as C. From e59bdf37b99b8ca9c5054bea748e60608d363f35 Mon Sep 17 00:00:00 2001 From: Shivaraj B H Date: Tue, 2 Apr 2024 12:14:25 +0530 Subject: [PATCH 25/31] cabal is not supported by highlightjs Co-authored-by: Sridhar Ratnakumar <3998+srid@users.noreply.github.com> --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 264308b7..3c745d94 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -120,7 +120,7 @@ cd .. && cargo build Just like any other dependency, you'll first add it to your `.cabal` file: -```cabal +```text executable hello-haskell -- ... extra-libraries: rust_nix_template From c4816d009d9feae966acc817a070ec3afd02493f Mon Sep 17 00:00:00 2001 From: Shivaraj B H Date: Tue, 2 Apr 2024 12:14:41 +0530 Subject: [PATCH 26/31] Rust with a capital R Co-authored-by: Sridhar Ratnakumar <3998+srid@users.noreply.github.com> --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 3c745d94..0e839788 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -140,7 +140,7 @@ You'll likely encounter an error like this: ... ``` -The easiest solution might seem to be `export LIBRARY_PATH=../target/debug`. However, this is not reproducible and would mean running an additional command to setup the prerequisite to build the Haskell package. Even worse if the rust project is in a different repository. +The easiest solution might seem to be `export LIBRARY_PATH=../target/debug`. However, this is not reproducible and would mean running an additional command to setup the prerequisite to build the Haskell package. Even worse if the Rust project is in a different repository. Often, the easiest solution isn't the simplest. Let's use Nix to simplify this process. From 345302c5f761bb01759a5a5876be162f2c5bb69b Mon Sep 17 00:00:00 2001 From: Shivaraj B H Date: Tue, 2 Apr 2024 12:15:21 +0530 Subject: [PATCH 27/31] link to haskell-flake dependency documentation Co-authored-by: Sridhar Ratnakumar <3998+srid@users.noreply.github.com> --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 0e839788..9e920d01 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -152,7 +152,7 @@ error: function 'anonymous lambda' called without required argument 'rust_nix_te ... ``` -To specify the Rust project as a dependency, let's edit `hello-haskell/default.nix` to: +To specify the Rust project as a dependency, we [setup haskell-flake dependency overrides](https://community.flake.parts/haskell-flake/dependency) by editing `hello-haskell/default.nix` to: ```nix { From ae455c026a8b1dc553677fa4a1a6df1cb1eed675 Mon Sep 17 00:00:00 2001 From: Shivaraj B H Date: Tue, 2 Apr 2024 12:15:44 +0530 Subject: [PATCH 28/31] wiki-link to macos Co-authored-by: Sridhar Ratnakumar <3998+srid@users.noreply.github.com> --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 9e920d01..4139da2f 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -182,7 +182,7 @@ nix run .#hello-haskell You should see the output `Hello, from rust!`. > [!note] MacOS caveat -> If you are on MacOS, the Haskell package will not run because `dlopen` will be looking for the `.dylib` file in the temporary build directory (`/private/tmp/nix-build-rust-nix...`). To fix this, you need to include [fixDarwinDylibNames](https://github.com/NixOS/nixpkgs/blob/af8fd52e05c81eafcfd4fb9fe7d3553b61472712/pkgs/build-support/setup-hooks/fix-darwin-dylib-names.sh) in `flake.nix`: +> If you are on [[macos]], the Haskell package will not run because `dlopen` will be looking for the `.dylib` file in the temporary build directory (`/private/tmp/nix-build-rust-nix...`). To fix this, you need to include [fixDarwinDylibNames](https://github.com/NixOS/nixpkgs/blob/af8fd52e05c81eafcfd4fb9fe7d3553b61472712/pkgs/build-support/setup-hooks/fix-darwin-dylib-names.sh) in `flake.nix`: > >```nix >{ From 7b19da40aaef5ed1d47c726431ce8e39e16f8516 Mon Sep 17 00:00:00 2001 From: Shivaraj B H Date: Tue, 2 Apr 2024 12:15:59 +0530 Subject: [PATCH 29/31] macOS Co-authored-by: Sridhar Ratnakumar <3998+srid@users.noreply.github.com> --- en/haskell-rust-ffi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 4139da2f..c32c84e6 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -181,7 +181,7 @@ nix run .#hello-haskell You should see the output `Hello, from rust!`. -> [!note] MacOS caveat +> [!note] macOS caveat > If you are on [[macos]], the Haskell package will not run because `dlopen` will be looking for the `.dylib` file in the temporary build directory (`/private/tmp/nix-build-rust-nix...`). To fix this, you need to include [fixDarwinDylibNames](https://github.com/NixOS/nixpkgs/blob/af8fd52e05c81eafcfd4fb9fe7d3553b61472712/pkgs/build-support/setup-hooks/fix-darwin-dylib-names.sh) in `flake.nix`: > >```nix From b28fabe260c971bfed4d7dea3aa84c8106321ffc Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 2 Apr 2024 12:38:11 +0530 Subject: [PATCH 30/31] init Haskell project with `nixpkgs` from flake registry suggested by @srid: https://github.com/nixos-asia/website/pull/24#discussion_r1547175111 --- en/haskell-rust-ffi.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index 8bf656d3..a51b2e18 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -49,14 +49,10 @@ After running `cargo build`, you should find a `librust_nix_template.dylib`[^hyp {#init-haskell} ## Initialize Haskell Project -Fetch `cabal-install` and `ghc` from the `nixpkgs` input of current flake and initialize a new Haskell project[^why-proj-nixpkgs]: - -[^why-proj-nixpkgs]: This approach ensures reproducibility as `cabal init` uses the version of GHC to initialize the `base` package constraints. +Fetch `cabal-install` and `ghc` from the `nixpkgs` in [flake registry](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-registry.html) and initialize a new Haskell project: ```sh -nixpkgs_url="github:nixos/nixpkgs/$(nix flake metadata --json | nix run nixpkgs#jq -- '.locks.nodes.nixpkgs.locked.rev' -r)" && \ -nix shell $nixpkgs_url#cabal-install $nixpkgs_url#ghc -c \ -cabal init -n --exe -m --simple hello-haskell +nix shell nixpkgs#ghc nixpkgs#cabal-install -c cabal -- init -n --exe -m --simple -d base --overwrite ``` {#nixify-haskell} From 3f40f65833319ec010a5360da8edf6760f555547 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 2 Apr 2024 13:38:50 +0530 Subject: [PATCH 31/31] misc. fixes --- en/haskell-rust-ffi.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/en/haskell-rust-ffi.md b/en/haskell-rust-ffi.md index d8d00466..9870f4b5 100644 --- a/en/haskell-rust-ffi.md +++ b/en/haskell-rust-ffi.md @@ -27,7 +27,7 @@ just run {#rust-lib} ## Create a Rust Library -The template we've initialized is a binary project, but we need a library project. The library should export a function callable from Haskell. For simplicity, let's export a function named `hello` that returns a `C-style string`. To do so, create a new file named `src/lib.rs` with the following contents: +The template we've initialized is a binary project, but we need a library project. The library should export a function callable from Haskell. For simplicity, let's export a function named `hello` that returns a `C-style string`. To do so, create a new file named `src/lib.rs` with the following contents and `git add src/lib.rs`: [[haskell-rust-ffi/lib.rs]] ![[haskell-rust-ffi/lib.rs]] @@ -52,7 +52,7 @@ After running `cargo build`, you should find a `librust_nix_template.dylib`[^hyp Fetch `cabal-install` and `ghc` from the `nixpkgs` in [flake registry](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-registry.html) and initialize a new Haskell project: ```sh -nix shell nixpkgs#ghc nixpkgs#cabal-install -c cabal -- init -n --exe -m --simple -d base --overwrite +nix shell nixpkgs#ghc nixpkgs#cabal-install -c cabal -- init -n --exe -m --simple hello-haskell -d base --overwrite ``` {#nixify-haskell} @@ -73,6 +73,7 @@ Additionally, add the following to `flake.nix`: # Inside `mkFlake` { imports = [ + inputs.haskell-flake.flakeModule ./hello-haskell ]; }; @@ -114,7 +115,7 @@ cd .. && cargo build {#add-rust-lib} ## Add Rust Library as a Dependency -Just like any other dependency, you'll first add it to your `.cabal` file: +Just like any other dependency, you'll first add it to your `hello-haskell/hello-haskell.cabal` file: ```text executable hello-haskell