jvoegele

jvoegele

Bond - Design by Contract for Elixir

Announcing Bond, Design by Contract for Elixir

Bond provides support for contract programming (also known as “Design by Contract”) for Elixir.

The primary goal for Bond is to provide the most feature-complete and thoroughly documented Design by Contract library for Elixir, with a concise and flexible syntax for specifying contracts.

Current and planned features include:

  • Function preconditions with @pre
  • Function postconditions with @post
  • “old” expressions in postconditions
  • “check” expressions for arbitrary assertions within a function body
  • Predicates (such as implies? and xor) for use in assertions
  • Detailed assertion failure reporting
  • Incorporation of preconditions and postconditions into @doc for function
  • Conditional compilation of contracts per environment
  • More detailed assertion failure reporting, including color coding à la ExUnit
  • Invariants for structs and/or stateful processes (if possible)

For comparison to other Design by Contract libraries for Elixir, please see the history section of the Bond documentation.

https://hexdocs.pm/bond/

Most Liked

sbuttgereit

sbuttgereit

Hi–

Interesting project. I saw this in the docs:

Contracts in the form of preconditions and postconditions are part of the public interface for a module in the same way that function signatures and typespecs are.

Is the intention that pre/post conditions replace traditional typespecs when used or should they be considered supplemental? If they are a replacement, do they interact with Dialyzer at all? Also, the set-theoretic types, assuming they land, also offer a manner of contract with functionality ramifications, are there thoughts about how this library would relate to that future?

I’ve not looked much beyond a quick search through the docs. But these are the questions I’d need to understand for adoption.

jvoegele

jvoegele

Hi @sbuttgereit,

Contracts are not intended to replace typespecs or set-theoretic types; rather, they are complementary mechanisms.

Typespecs are evaluated at compile-time or with a static analysis tool such as Dialyzer, whereas contracts as implemented by Bond are evaluated at run-time (although they are attached to functions at compile-time so must be valid Elixir code).

The closest analogue to contracts in native Elixir is guard expressions for functions, which allow for making rudimentary “assertions” about function arguments. Assertions in Bond are much more powerful and are not limited to the pre-defined set of functions that are allowed in guard expressions.

jarlah

jarlah

fun :slight_smile: <3

  @post is_hallo: result == :hallo
  def hello do
    :world
  end

in this case dialyzer is screaming at me already … but its MUCH easier to actually get the error in your face … than dialyzer warning about something that SHOULD fail … but never does. ref this thread where dialyzer warns about something that never fails Dialyzer warning from hell: no_return when calling erl_tar.create - #2 by jarlah

iex(1)> TestBond.hello()
** (Bond.PostconditionError) postcondition failed in TestBond.hello/0
|   label: :is_hallo
|   assertion: result == :hallo
|   binding: [result: :world]

    (test_bond 0.1.0) lib/test_bond.ex:6: TestBond.hello/0
    iex:1: (file)
iex(1)> 

its probably more valuable in the ecto example i described above

jvoegele

jvoegele

Thanks for the feedback @dimitarvp.

You might be right about stateful assertions becoming messy. I had been thinking about invariants as inherently stateful, and which describe properties of the state before and after state transitions. But as you point out, there is probably still value in stateless invariants that use only function arguments.

For example, consider a bounded stack with a capacity fixed at creation time. Such stacks do not allow new items to be pushed if the stack is already full. I can think of a few invariants that I would express at the module level, even for a purely functional, stateless version of such a stack (ignore for now how the stack variable binding could be injected into the assertions, I’ll come back to that):

@invariant size_limited_by_capacity: size(stack) <= capacity(stack),
           non_negative_size: size(stack) >= 0,
           fixed_capacity: capacity(stack) == old(capacity(stack)),
           full_implies_at_capacity: full?(stack) ~> (size(stack) == capacity(stack))

Many of these invariant properties could be expressed as postconditions on the relevant functions, but expressing them at the module level as invariants brings to mind a couple of possible advantages:

  1. The invariants could be checked at run-time before and after every function call.
  2. The invariants could be added to the @moduledoc in a similar way that preconditions and postconditions are already added to the @doc for functions.

The main complication that I can think of is that this scheme would probably only work for 1-arity functions that use the same name for the lone parameter. Otherwise, what would be the value bound to the stack variable in the invariant assertions?

I’m also not sure if the old expression in the fixed_capacity assertion in the invariant makes sense. If invariants are to be checked before and after function calls, the old expression would fail when evaluated before the function call (at least the way that old is currently implemented in Bond). I don’t think Eiffel allows old to be used in invariants, presumably for this reason.

Curious about your thoughts, or if you (or anyone else) have other ideas about invariants for purely functional code.

jvoegele

jvoegele

I added a new “Contracts in a Concurrent World” guide to the Bond docs, which discusses how to eliminate the need for stateful assertions:

https://hexdocs.pm/bond/contracts-and-concurrency.html

TL;DR - It’s the same old trick used for better testability: separate the pure functional code from stateful code.

Where Next?

Popular in Libraries Top

tompave
Hello there, I would like to share a feature toggles library (AKA feature flags) I’ve been working on. The main package is FunWithFlags...
New
deadtrickster
I’ve just released stable versions of my Prometheus Elixir libs: Elixir client [docs]; Ecto collector [docs]; Plugs instrumenter/Export...
New
mathieuprog
Hello :wave: Allow me to introduce you to Tz, an alternative time zone database support to Tzdata. Why another library? First and fore...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
martinthenth
Hello everybody :wave: Recently, some of my colleagues talked about database ids and uuids and their problems, and I remembered the pain...
New
bryanjos
Hi, I just published version 0.23.0 of Elixirscript. Most of the changes are around JavaScript interop now that Elixirscript uses the ...
New
mtrudel
Bandit is an HTTP server for Plug and WebSock apps. Bandit is written entirely in Elixir and is built atop Thousand Island. It can serve...
New
mattludwigs
Grizzly is a library for working with Z-Wave devices. Z-Wave is a low-frequency radio protocol for controlling smart home devices on a me...
New
bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
381 12391 119
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 45766 226
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New

Sub Categories:

We're in Beta

About us Mission Statement