croma v0.6.0 Croma.Defun
Module that provides Croma.Defun.defun/2 macro.
Summary
Macros
Defines a function together with its typespec. This provides a lighter-weight syntax for functions with type specifications and functions with multiple clauses
Defines a private function together with its typespec.
See defun/2 for usage of this 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
Macros
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 during compilation.
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 based on their types
You can instrument check of preconditions on arguments by specifying argument’s 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 validate/1 at the top of its body:
@spec f(MyString.t) :: atom
def f(s)
def f(s) do
s = case MyString.validate(s) do
{:ok , value } -> value
{:error, reason} -> raise "..."
end
String.to_atom(s)
end
The generated code assumes that validate/1 function is defined in the same module as the specified type.
Generating validation of arguments 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.
tryblock is not implicitly started in body ofdefun, in contrast todef.
Defines a private function together with its typespec.
See defun/2 for usage of this 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.