# Partially apply function accepting multiple modules and keep polymorphic types

**URL:** <https://discuss.ocaml.org/t/partially-apply-function-accepting-multiple-modules-and-keep-polymorphic-types/13823>\
**Category:** Learning\
**Created:** [January 9, 2024, 2:49pm UTC](https://discuss.ocaml.org/t/partially-apply-function-accepting-multiple-modules-and-keep-polymorphic-types/13823 "2024-01-09T14:49:37Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![mudrz](https://avatars.discourse-cdn.com/v4/letter/m/8e7dd6/32.png) [@mudrz](https://discuss.ocaml.org/u/mudrz)\
**Post date:** [January 9, 2024, 2:49pm UTC](https://discuss.ocaml.org/t/partially-apply-function-accepting-multiple-modules-and-keep-polymorphic-types/13823/1 "2024-01-09T14:49:37Z")

</div>

We have a functor `Make` that takes a `Base` module, and a second `S` module.  
Module `Base` does not have types and does not affect how `Make` works. Module `S` however changes what types `do_things` works with:

```ocaml
module type Base = sig val v : int end

module type S = sig
  type a
  val to_a : string -> a
end

module Make (B : Base) (X : S) = struct
  let do_things a_to_string s =
    let a = X.to_a s in
    a_to_string a
end

```

if we have a function

```ocaml
let make (type a) (module B : Base) (module X : S with type a = a)
    (a_to_string : a -> string) =
  let module M = Make (B) (X) in
  M.do_things a_to_string

```

we can use it to instantiate `Make` with multiple types:

```auto
module Int_s = struct
  type a = int
  let to_a = Stdlib.int_of_string
end

module Float_s = struct
  type a = float
  let to_a = Stdlib.float_of_string
end

module B = struct let v = 1 end
let m1 = make (module B) (module Int_s)
let m2 = make (module B) (module Float_s)

```

This works, but why pass `module B` every time, if `type a` is not dependent on it - we can partially apply it:

```auto
let make2 = make (module B)

```

however this no longer works:

```ocaml
let m11 = make2 (module Int_s)
let m22 = make2 (module Float_s)

```

after the first `m11` call - `make2` only works with `int`

I thought moving the `type a` specifier could work:

```ocaml
let make (module B : Base) (type a) (module X : S with type a = a)
    (a_to_string : a -> string) =
  let module M = Make (B) (X) in
  M.do_things a_to_string

```

But this doesn’t seem to make a difference. Is there any way to partially apply `Base`, but not tie `make` with the type of the first module `S` that is passed?

---

<div class="post-metadata">

**Author:** ![vlaviron](https://avatars.discourse-cdn.com/v4/letter/v/ec9cab/32.png) [@vlaviron](https://discuss.ocaml.org/u/vlaviron)\
**Post date:** [January 9, 2024, 2:58pm UTC](https://discuss.ocaml.org/t/partially-apply-function-accepting-multiple-modules-and-keep-polymorphic-types/13823/2 "2024-01-09T14:58:29Z")

</div>

This looks like the value restriction.  
Consider this simpler example:

```ocaml
# let f _ignored x = x;;
val f : 'a -> 'b -> 'b = <fun>
# f 0 1;;
- : int = 1
# f 0 true;;
- : bool = true
# let f2 = f 0;;
val f2 : '_weak1 -> '_weak1 = <fun>
# f2 1;;
- : int = 1
# f2 true;;
Error: This expression has type bool but an expression was expected of type
         int

```

The usual solution is eta-expansion (in your case, `let make2 arg = make (module B) arg`).  
You can find more about the value restriction in [the relevant section](https://v2.ocaml.org/manual/polymorphism.html) of the manual.

---

<div class="post-metadata">

**Author:** ![hyphenrf](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/hyphenrf/32/2733_2.png) [@hyphenrf](https://discuss.ocaml.org/u/hyphenrf)\
**Post date:** [January 9, 2024, 7:58pm UTC](https://discuss.ocaml.org/t/partially-apply-function-accepting-multiple-modules-and-keep-polymorphic-types/13823/3 "2024-01-09T19:58:35Z")

</div>

The tl;dr (of the manual section) that glosses over semantic reasoning but is a good rule of thumb:  
you can’t have “polymorphic values”, even if said values are partial-applications of functions.

This is value syntax:

```ocaml
let make2 = make (module B)

```

This is function syntax:

```ocaml
let make2 = fun m -> make (module B) m

```

The rule of thumb is purely syntactic. Function syntax is one of:

```ocaml
let f ... = ...
let f = fun ... -> ...
let f = function ... -> ...

```

You really should read the manual section for details, reasoning, semantics, etc…  
Take note that `make` is evaluated in the first example but not the second, this is a key detail.

---

<div class="post-metadata">

**Author:** ![mudrz](https://avatars.discourse-cdn.com/v4/letter/m/8e7dd6/32.png) [@mudrz](https://discuss.ocaml.org/u/mudrz)\
**Post date:** [January 10, 2024, 1:17pm UTC](https://discuss.ocaml.org/t/partially-apply-function-accepting-multiple-modules-and-keep-polymorphic-types/13823/4 "2024-01-10T13:17:59Z")

</div>

thanks! this was a helpful read, I ended up defining the function inside of a functor, so that Base can be partially applied, and then passing the resulting make through other functors:

```ocaml
module Make (D: sig module B: Base end) = struct let make (module X:...) end

```

`let make2 arg = make (module B) arg)` didn’t work well for my use case, because in my real case I had to do something like

```auto
let make2 (type a b c) (module X : S with type a = a and type b = b and type c = c) arg
  = make (module Base) (module X) arg

```

at multiple places

---

<div class="post-metadata">

**Author:** ![vlaviron](https://avatars.discourse-cdn.com/v4/letter/v/ec9cab/32.png) [@vlaviron](https://discuss.ocaml.org/u/vlaviron)\
**Post date:** [January 10, 2024, 2:01pm UTC](https://discuss.ocaml.org/t/partially-apply-function-accepting-multiple-modules-and-keep-polymorphic-types/13823/5 "2024-01-10T14:01:34Z")

</div>

Great to know that you’ve solved your problem. Here are a few follow-up questions if you have a bit more time:

> [@mudrz](#):
>
> `let make2 arg = make (module B) arg)` didn’t work well for my use case, because in my real case I had to do something like
> 
> ```auto
> let make2 (type a b c) (module X : S with type a = a and type b = b and type c = c) arg
> = make (module Base) (module X) arg
> 
> ```
> 
> at multiple places

I’m a bit surprised that you need something that heavy. First-class modules can be bound to regular variables too:

```ocaml
# module type T = sig type t val x : t end;;
module type T = sig type t val x : t end
# let mk (type a b) (module X : T with type t = a) (module Y : T with type t = b) f = f X.x Y.x;;
val mk :
  (module T with type t = 'a) ->
  (module T with type t = 'b) -> ('a -> 'b -> 'c) -> 'c = <fun>
# module I = struct type t = int let x = 0 end;;
module I : sig type t = int val x : int end
# let mk2 arg = mk (module I) arg;;
val mk2 : (module T with type t = 'a) -> (I.t -> 'a -> 'b) -> 'b = <fun>

```

The `arg` variable in the definition of `mk2` is a first-class module, and the type inferred is the same as if local types were used.

Also, I’m not sure why your functor takes as argument `(D: sig module B: Base end)` instead of just `(B : Base)`; although if you need more than just one module in the functor parameter it makes sense to wrap all of that.

---

<div class="post-metadata">

**Author:** ![mudrz](https://avatars.discourse-cdn.com/v4/letter/m/8e7dd6/32.png) [@mudrz](https://discuss.ocaml.org/u/mudrz)\
**Post date:** [January 10, 2024, 4:17pm UTC](https://discuss.ocaml.org/t/partially-apply-function-accepting-multiple-modules-and-keep-polymorphic-types/13823/6 "2024-01-10T16:17:23Z")

</div>

this works perfectly…  
I am used to typing types everywhere, and didn’t realize that I can just let OCaml infer the modules and arguments types.  
Thank you!

in the contrived example above:

```ocaml
let make3 a b = make (module B) a b
let m111 = make3 (module Int_s)
let m222 = make3 (module Float_s)

```
