Croma.Defun (croma v0.11.1) View Source
Module that provides Croma.Defun.defun/2 macro.
Link to this section Summary
Functions
Defines a function together with its typespec.
Defines a private function together with its typespec.
Defines a unit-testable private function together with its typespec.
Link to this section Functions
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}"
endThe code above is expanded to the following function definition.
@spec f(integer, String.t) :: String.t
def f(a, b) do
"#{a} #{b}"
endFunction 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)]
endis 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)]
endPattern 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}"
endthen 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}"
endGenerating 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}"
endis 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}"
endFor 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)
endbecomes 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)
endThe 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.
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.