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

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
psy-q
I’m trying to set up Emacs with elixir-ls via lsp-mode and credo via Flycheck. This should mostly be preconfigured as Flycheck picks up c...
New

Other Trending Topics Top

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
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews