Croma.Defun (croma v0.12.0)
View SourceModule that provides Croma.Defun.defun/2
macro.
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.
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}"
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 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
.