tjchambers

tjchambers

I am using 1.20.0-rc4 and discovered a bug I introduced today. Wanted to share mostly because I want to understand the extent of how set theoretic typing will or will not address this kind of bug. The code snippet:

result = Cachex.incr(:cache, :key, 1, default: 0)

if result > 5 do 

…

end

What I forgot in my haste was that the return from that Cachex function is a tuple, {:ok, integer()} not integer(). And I got no warning. Had I done something similar in a guard expression comparing a tuple to an integer IMHO would not have indicated any warning under set theoretic typing.

So a function signature like:

def x(y) when y >= 5, do: nil

where y is a type of tuple should NOT generate a type warning. It should not only because of the Elixir type heirarchy.

It seems this is a very possible common bug when dealing with libraries where Bang methods tend to return some Elixir type and non-bang functions tend to return a tuple with the second element being some Elixir type and the first element being an atom such as :ok, or :error.

I am interest in others thinking, and mostly to help me understand what can or cannot be done to help locate these errors. If all of our libraries we use day in and day out had set theoretic typing contribution to our app compilations, this seems to be something of value.

Anyway - please comment and help me reason about this.

Showing Posts 1 to 10

cmo

cmo

It’s a gotcha for sure. I always add an is_integer(x) to guards that do less/greater than checks.

def x(y) when is_integer(y) and y >= 5, do: nil

NobbZ

NobbZ

I’d not expect a typewarning here, but rather a “is always true/false” kind of warning, if indeed y is always a tuple.

tjchambers

tjchambers OP

Here is I believe a test case:

defmodule TestTuple do
  @moduledoc """
  A simple module to demonstrate tuple usage in Elixir.
  """
@doc """
  Takes a tuple with two elements 
  """
@spec format_tuple({any(), any()}) :: String.t()
def format_tuple({_first, _second} = tuple) do
  x(tuple)
end

defp x(tuple) when tuple >= 5 do
 "This is the tuple: #{inspect(tuple)}"
end

defp x(tuple) do
 "Tuple is less than 5: #{inspect(tuple)}"
end

end

and results are

iex [03:36 :: 1] > TestTuple.format_tuple({1,2})
“This is the tuple: {1, 2}”

No compile error or warnings.

So I would say this scenario is undetected by current set theoretic typing compilation.

cmo

cmo

I believe you need to do something to indicate the type for the compiler to catch it. e.g. Atom.to_string(x) tells the compiler that x is an atom.

You’re telling it there is a tuple. >= is not an integer only operator so it’s not going to complain about that. Maybe if you put an is_integer guard with it it might tell you that clause is unused. Dunno if that is or will be implemented.

manhvu

manhvu

Type specs and set-theoretic types don’t raise a warning here because any types can be compared (this comes from Erlang—I’m not sure about the reasoning behind that :thinking:). Sometimes type checks are missed before comparing data, which leads to logic errors.

LostKobrakai

LostKobrakai

Once you remove the function indirection there is a warning though - because the tuple will always result in the same comparison result. The issue seems to be with the type knowledge propagating through the callstack.

matt-savvy

matt-savvy

That’s because tuple >= 5 is not only valid, but returns true.

iex(1)> tuple = {1, 2}
{1, 2}
iex(2)> tuple >= 5
true

Elixir allows comparisons between elements of different types.

See Kernel — Elixir v1.21.0-dev

TLDR; any tuple is considered to be greater than any integer.

matt-savvy

matt-savvy

In this case, you compared a tuple like {:ok, 1} > 5, which will always return true.

Also, every atom is considered greater than every number, so if you were using a function that returned {:ok, integer() | :error} , either way, result > 5 is always going to return true.

This is one place where I hope eventually we’ll be able to use types, to show a warning that this expression is always going to return true .

tjchambers

tjchambers OP

Exactly my point @matt-savvy . Because there is an Elixir type heirarcy in play, it seems as though we lose some ability to detect what I consider to be a bug, because precisely it is a valid comparison.

tjchambers

tjchambers OP

I don’t feel like I was making myself clear here. I would hope eventually in this situation that given the subsequent function, that it would announce that subsequent function would never be reached. Even as confusing at first as that may seem.

Ideally (in my selfishness) I would like it to tell me more precisely that I was comparing disparate types, but the language type hierarchy already permits that comparison. If somehow that type hierarchy could be indicated to not be in play (comparing for example atoms to tuples to lists with greater than or less than) it would increase the value of type checking.

I do realize if I indicated the expected type of the parameters with a guard then I would be encouraging the type checking accuracy. However one of the purposes afaict of the lovely approach to gradual theoretic typing is NOT relying on code changes or type hinting. So far it seems this propagation of type knowledge has been IMHO highly (remarkably?) successful.

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews