Hi, I am designing a library around data structures, so the design question is would t comes as the first argument or last be a better choice? Is there any deep reason behind your argument, would love to hear about your opinions.
t comes last, good for pipe |>
t comes first, similar to other languages, like golang method
I recommend making t an anonymous argument, and naming all other arguments. Just like option (1), this allows pipeing, and it makes code much easier to read. This is the style adopted in Base/Core.
A function with one anonymous argument and several named arguments is just as composable as a function with several anonymous arguments: the former composes on the unique anonymous argument, the latter on the final anonymous argument. (I would challenge you to come up with a convincing practical counter example.)
Regarding verbosity, my experience is that there is little to no difference. For example, functions are always named ~f in Base/Core, a very concise but descriptive name. Moreover, OCaml has some nice syntax sugar for the comon case that the name of the argument matches a variable in scope: you can write foo x ~default instead of foo x ~default:default. Finally, I would argue that the more readable code allows for shorter variable names/less documentation in practice, so it’s really a win-win.
You could be interesting by the convention from the Edison library in Haskell which took advises from the Chris Okazaki’s book about purely functional data structures.
And, from hat I know about standard library, your second convention is used when we manipulate a mutable data-structure (like Hashtbl). Containers seems to follow this convention.
I prefer both to diff mutable data-structure and immutable data-structure to allow the user to compose as you said the second one but disallow for the first one and use a sequence: add t key value; add t key value; ...
I would say it depends on how your functions are intended to be used. From my point of view, the general form of a function’s type is a -> b, i.e. it is something that transforms an object of type a to an object of type b: a morphism. So when I see a function with the type a -> b -> c, I see it as a family of morphisms from type b to type c parametrized by a value of type a. Therefore, if you want a family of morphisms that operate on your type t, you put it last, but if it is your type that parameterized a family of morphisms (as with regex for instance) you put it first.
If I take this two examples from the standard lib:
List.map;;
- : ('a -> 'b) -> 'a list -> 'b list = <fun>
List.nth;;
- : 'a list -> int -> 'a = <fun>
I consider the first one has a good type: we transform a morphism from a to b to a morphism from a list to b list (the type constructor list is a functor, and map is the fmap of the functor type classes in Haskell). But for the second one, I would gave it the type int -> 'a list -> 'a considering this is only a generalization of hd : 'a list -> 'a (== List.nth 1).
My general method is to stick to one approach until I realize it makes more sense to go the other way, then half-heartedly switch over some of the functions, and eventually end up with a mess. Works for me!
I really prefer the method they use at janestreet with t being an anonymous argument with any other arguments being named. You get the added benefit of self documentation.
One language I feel gets this right is Elixir. If the function is on the right side of the |> then the result of the left side is injected as the first argument of the function.