# `Croma.Defun`
[🔗](https://github.com/skirino/croma/blob/main/lib/croma/defun.ex#L1)

Module that provides `Croma.Defun.defun/2` macro.

# `defun`
*macro* 

Defines a function together with its typespec.

This provides a lighter-weight syntax for functions with type specifications and functions with multiple clauses.

## Example

The following examples assume that `Croma.Defun` is imported
(you can import it by `use Croma`).

    defun f(a :: integer, b :: String.t) :: String.t do
      "#{a} #{b}"
    end

The code above is expanded to the following function definition.

    @spec f(integer, String.t) :: String.t
    def f(a, b) do
      "#{a} #{b}"
    end

Function with multiple clauses and/or pattern matching on parameters can be defined
in the same way as `case do ... end`:

    defun dumbmap(as :: [a], f :: (a -> b)) :: [b] when a: term, b: term do
      ([]     , _) -> []
      ([h | t], f) -> [f.(h) | dumbmap(t, f)]
    end

is converted to

    @spec dumbmap([a], (a -> b)) :: [b] when a: term, b: term
    def dumbmap(as, f)
    def dumbmap([], _) do
      []
    end
    def dumbmap([h | t], f) do
      [f.(h) | dumbmap(t, f)]
    end

## Pattern matching on function parameter and omitting parameter's type

If you omit parameter's type, its type is infered from the parameter's expression.
Suppose we have the following function:

    defun f(%MyStruct{field1: field1, field2: field2}) :: String.t do
      "#{field1} #{field2}"
    end

then the parameter type becomes `MyStruct.t`.

    @spec f(MyStruct.t) :: String.t
    def f(a1)
    def f(%MyStruct{field1: field1, field2: field2}) do
      "#{field1} #{field2}"
    end

## Generating guards from argument types

Simple guard expressions can be generated by `defun/2` using `g[type]` syntax.
For example,

    defun f(s :: g[String.t], i :: g[integer]) :: String.t do
      "#{s} #{i}"
    end

is converted to the following function with `when is_integer(i)` guard.

    @spec f(String.t, integer) :: String.t
    def f(s, i)
    def f(s, i) when is_binary(s) and is_integer(i) do
      "#{s} #{i}"
    end

For supported types of guard-generation please refer to the source code of `Croma.Guard.make/3`.

Guard generation can be disabled by setting application config at compile time.
For example, by putting the following into `config/config.exs`,

    config :croma, [
      defun_generate_guard: false
    ]

then `g[String.t]` becomes semantically the same as `String.t`.

## Validating arguments and return value based on their types

You can instrument check of pre/post conditions by specifying type as `v[type]`.
For instance,

    defmodule MyString do
      use Croma.SubtypeOfString, pattern: ~r/^foo|bar$/
    end

    defun f(s :: v[MyString.t]) :: atom do
      String.to_atom(s)
    end

becomes the following function definition that calls `valid?/1` at the top of its body:

    @spec f(MyString.t) :: atom
    def f(s)
    def f(s) do
      if !MyString.valid?(s) do
        raise "..."
      end
      String.to_atom(s)
    end

The generated code assumes that `valid?/1` function is defined in the type module of the specified type.
For primitive types croma defines their type modules and thus you can freely use e.g. `v[integer]`.

Generating validation of arguments and return values can be disabled by setting application config during compilation.

    config :croma, [
      defun_generate_validation: false
    ]

## Known limitations
- Overloaded typespecs are not supported.
- Guard generation and validation are not allowed to be used with multi-clause syntax.
- Using unquote fragment in parameter list is not fully supported.
- `try` block is not implicitly started in body of `defun`, in contrast to `def`.

# `defunp`
*macro* 

Defines a private function together with its typespec.

See `defun/2` for usage of this macro.

# `defunpt`
*macro* 

Defines a unit-testable private function together with its typespec.

See `defun/2` for usage of this macro.
See also `Croma.Defpt.defpt/2`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
