
Fl4m3Ph03n1x
How to typespec guards in a human friendly way?
Background
I am a fan of dialyzer and friends (looking at Gradient) and I try to have sepcs in my code as much as I can. To this end, I am playing with guards and I want my guard definitions to also have a typespec:
defmodule AuctionHouse.Shared.ExtraGuards do
@moduledoc """
Contains additional guards to use in functions.
"""
defguard is_pos_integer(value) when is_integer(value) and value > 0
end
Problem
So, now that I have this simple guard, I want a spec for it. However, dyalizer’s suggestion doesn’t strike me as exactly human readable.
@spec is_pos_integer(any) ::
{:__block__ | {:., [], [:andalso | :erlang, ...]}, [],
[{:= | {any, any, any}, list, [...]}, ...]}
defguard is_pos_integer(value) when is_integer(value) and value > 0
I believe this is likely defined as a function that takes any
as an argument but the return type is very difficult for me to understand. I assume it means it creates erlang code, like a macro, but I can’t make sense of it.
Questions
- What does the return type mean?
- Is there a way to make this more human readable? If so, how?
Marked As Solved

Fl4m3Ph03n1x
Solution
What’s happening here is that defguard
receives code and returns code (to be more specific, it both receives a AST representation of code and then returns that AST modified). As as consequence, this means that the correct typespec would be this:
@spec is_pos_integer(Macro.t()) :: Macro.t()
Which many people in the community defend is not very useful.
I agree with this consensus, so the next best option is to add a @doc
to the guard to clearly document it, and is what I suggest as well.
Sources:
Popular Backend topics










Other popular topics










Latest in Backend
Latest (all)
Categories:
Popular Portals
- /elixir
- /opensuse
- /rust
- /kotlin
- /ruby
- /erlang
- /python
- /clojure
- /react
- /quarkus
- /go
- /vapor
- /v
- /react-native
- /wasm
- /django
- /security
- /nodejs
- /centos
- /rails
- /haskell
- /fable
- /gleam
- /js
- /swift
- /deno
- /tailwind
- /assemblyscript
- /laravel
- /symfony
- /phoenix
- /crystal
- /typescript
- /debian
- /adonisjs
- /julia
- /arch-linux
- /svelte
- /spring
- /preact
- /flutter
- /c-plus-plus
- /actix
- /java
- /angular
- /ocaml
- /zig
- /kubuntu
- /scala
- /zotonic
- /vim
- /rocky
- /lisp
- /keyboards
- /html
- /vuejs
- /nim
- /emacs
- /elm
- /nerves