Hacker News new | past | comments | ask | show | jobs | submit login

Here's clamp in idiomatic Elixir (using multi-clause functions and guards):

  def clamp(min, _max, n) when n<min, do: min
  def clamp(_min, max, n) when n>max, do: max
  def clamp(_min, _max, n), do: n



An Elixir convention I've seen is to put the thing you're operating on first, so that you can compose functions using the `|>` operator, which places the previous expression as the first argument of the function to the right.

Maybe something like this?

  defmodule Compare do
    def clamp(number, minimum, maximum) do
      number
      |> max(minimum)
      |> min(maximum)
    end
  end
  
  import Compare
  
  clamp(5, 1, 10) # 5
  clamp(1, 5, 10) # 5
  clamp(10, 1, 5) # 5
  
  
  some_number
  |> clamp(min, max)

As a side note, I think the Elixir |> operator is a stroke of genius that other languages should take a look at. Making the pipe operator append the _first_ argument has the following benefits

1.) It makes the most "important" argument of the function the first thing you read in function signatures

2.) If you need to add more arguments to a function signature later, they tend to be less important the original args, so they tend to make sense at the end

3.) It creates a convention for all libraries to follow so they can leverage the pipe operator. Its really jarring when the thing you want to put in a pipeline isn't the first argument (looking at you `Regex`[0] which puts the regular expression as the first arg and not the string)

[0] https://hexdocs.pm/elixir/Regex.html#replace/4


> It makes the most "important" argument of the function the first thing you read in function signatures

Doesn't that make writing functions that can use partial application harder? e.g. If I was writing clamp i would want the signature to be

     (defn clamp [min max n] ,,,)
Then I can do:

    (map (partial clamp 1 11) [-14 2 5 8 11 15 18])
I know when I use Clojures threading macros I use thread last way more than any of the others. My next most common would be piping it into arbitrary locations, e.g.:

    ; pipe into an arbitrary spot (specified here as o)
    (as-> (range 1 10) o
          (map inc o)
          (filter even? o)
          (reduce + o))

I rarely use thread-first.


Elixir has an anonymous function short hand that makes the "partial application" use case pretty easy for me.

  &Compare.clamp(&1, some_min, some_max)
The above creates an arity one function which puts the arg into the first position in the original function.

I've never felt pain around partial application because I use the anonymous function short hand a good deal


Yes you are right! Puting the number in the first parameter is even more idiomatic Elixir.


  defmodule Math do
    def clamp(num, _min, max) when num > max, do: max
    def clamp(num, min, _max) when num < min, do: min
    def clamp(num, _min, _max), do: num
  end


Following xxs example of looking at NaN behavior, with this code, a bound (say lower) of NaN means that bound is disabled. Which may or may not be what you want.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: