Proposal: Kernel.boolean/1

240 views
Skip to first unread message

eksperimental

unread,
Oct 25, 2015, 5:02:59 AM10/25/15
to elixir-l...@googlegroups.com
Hi everyone,
I propose to have a Kernel.boolean/1 macro.
It will be allowed in guard tests.

since only `and`, `or` and `not` only accept booleans this macro
come in handy to use in conjunction with functions that will not return
boolean.

here's the code, along with same tests
https://gist.github.com/eksperimental/6de64dd3d384d5cdb255

please let me know what you guys think

Chris McCord

unread,
Oct 25, 2015, 9:45:41 AM10/25/15
to elixir-l...@googlegroups.com
It’s a nice idea, but we already have an `is_boolean/1` function that can be used in guards, so this would go against guard naming conventions, and also clash with the existing function. It also has different behavior than `is_boolean`, which returns `false` for nil. So I would vote -1 and favor explicit false/nil checks in these cases.
> --
> You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/20151025160245.72c62ef8.eksperimental%40autistici.org.
> For more options, visit https://groups.google.com/d/optout.

Aaron Sikes

unread,
Oct 25, 2015, 1:41:06 PM10/25/15
to elixir-l...@googlegroups.com
I think this role is already served by &&, ||, and !, no?

Maxim Chernyak

unread,
Oct 25, 2015, 1:53:54 PM10/25/15
to elixir-l...@googlegroups.com
If I understand correctly, this particular case can be solved by !! (double bang) fwiw.

On Oct 25, 2015, at 1:41 PM, Aaron Sikes <jinga...@gmail.com> wrote:

I think this role is already served by &&, ||, and !, no?


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

eksperimental

unread,
Oct 25, 2015, 2:08:54 PM10/25/15
to elixir-l...@googlegroups.com
hi everyone,
thank for the reponses.

Chris, is_boolean/1 and boolean/1 are two completely different
expressions.
what boolean does it to convert any expression to either
`true` or `false`, while is_boolean is return true if the expression is
`true` or `false`.

maybe my explanation that it can be used in guards made it confusion.
what i meant is that so far,
with the current functions that do not return a boolean, it is not easy
to use them in guards with the boolean operators `and`, `or`, `not`.

Aaron and Sikes:
those shortcircuit operators cannot be used in guards, only the boolean
versions mentioned above.
but as Maxim commented, this function can be solved by doing `not not`
which is not very readable in a guard and can also be misleading.

thanks for the input


On Sun, 25 Oct 2015 13:53:50 -0400
Maxim Chernyak <madfa...@gmail.com> wrote:

> If I understand correctly, this particular case can be solved by !!
> (double bang) fwiw.
>
> > On Oct 25, 2015, at 1:41 PM, Aaron Sikes <jinga...@gmail.com>
> > wrote:
> >
> > I think this role is already served by &&, ||, and !, no?
> >
> >
> > --
> > You received this message because you are subscribed to the Google
> > Groups "elixir-lang-core" group. To unsubscribe from this group and
> > stop receiving emails from it, send an email to
> > elixir-lang-co...@googlegroups.com
> > <mailto:elixir-lang-co...@googlegroups.com>. To view
> > <https://groups.google.com/d/msgid/elixir-lang-core/1445794862819.0ec18702%40Nodemailer?utm_medium=email&utm_source=footer>.
> > For more options, visit https://groups.google.com/d/optout
> > <https://groups.google.com/d/optout>.
>

eksperimental

unread,
Oct 26, 2015, 4:03:20 AM10/26/15
to elixir-l...@googlegroups.com
I think the proper name for this function should be

Kernel.to_boolean/1

Phani Mahesh

unread,
Oct 26, 2015, 5:12:46 AM10/26/15
to elixir-lang-core
Yes, `to_boolean/1` makes the intentions clear, and also fits the pattern of existing `to_string/1`, `to_chat_list/1`

José Valim

unread,
Oct 27, 2015, 12:50:00 PM10/27/15
to elixir-l...@googlegroups.com
I am not convinced we need this addition to Elixir. The nil and false cases are easy to express in matches and, if they need to be grouped, in/2.

In particular, both to_string/1 and to_char_list/1 in Kernel are powered by protocols, which does not mirror to_boolean and, as others said, using ! and !! already does the work.




José Valim
Skype: jv.ptec
Founder and Director of R&D

To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/20151026150314.030a9d11.eksperimental%40autistici.org.
For more options, visit https://groups.google.com/d/optout.

eksperimental

unread,
Nov 1, 2015, 7:35:50 AM11/1/15
to elixir-l...@googlegroups.com
Jose: Is it an Elixir convention that "to_*" functions are implemented
in protocols?

Just to know, since I will probably submit it to Crutches and any
other library that my extend Elixir, what would be a good name for it?
Kernel.bool/1 ?

Can I use Kernel.boolean/1 or that will overlap with the `boolean`
type in the typespecs?

thanks.

On Tue, 27 Oct 2015 17:49:39 +0100
José Valim <jose....@plataformatec.com.br> wrote:

> I am not convinced we need this addition to Elixir. The nil and false
> cases are easy to express in matches and, if they need to be grouped,
> in/2.
>
> In particular, both to_string/1 and to_char_list/1 in Kernel are
> powered by protocols, which does not mirror to_boolean and, as others
> said, using ! and !! already does the work.
>
>
>
>
> *José Valim*
> > For more options, visit https://groups.google.com/d/optout.
> >
>

José Valim

unread,
Nov 1, 2015, 8:48:28 AM11/1/15
to elixir-l...@googlegroups.com
Not necessarily , but the ones in Kernel are. :)

--


José Valim

Moxley Stratton

unread,
Dec 22, 2020, 7:21:36 PM12/22/20
to elixir-lang-core
I would like revisit this issue.

The issue was last discussed 5 years ago. Much has changed in that time. The given solution was to use a double-negative (!!) to convert a given value to a pure boolean.

Here are the problems with the suggested solution:
1. I believe this solution is a hack that is not intuitive to beginners. It's rarely seen by mid and senior developers, meaning they have to pause to interpret what !! means.
2. A double negative can't be part of an Elixir pipeline.
3. The Credo package warns about double negatives: "Double boolean negation found". Linters in other languages have a similar warning. Other languages have a built-in function (JavaScript has Boolean()) to convert to boolean.
4. The alternative to double-negative is verbose: "if expr, do: true, else: false", or the less readable "expr && true || false"

I would be happy to submit a PR to add Kernel.to_boolean/1.

José Valim

unread,
Dec 23, 2020, 2:48:39 AM12/23/20
to elixir-l...@googlegroups.com
Can we revisit in which scenarios an explicit boolean conversion is necessary?

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

Moxley Stratton

unread,
Dec 23, 2020, 10:32:02 AM12/23/20
to elixir-l...@googlegroups.com
Here are examples:

    def has_access? do
      at_least_logged_in? and !!get_csrf_cookie(session)
    end

    %{event_form | submitted: !!event_form.submitted_at}

    case !!(membership_ends_on || trial_ends_on || dues_exempt) do
      true -> changeset
      false -> add_error(changeset, :membership_ends_on, "is required")
    end

    case !!previous_value and !!new_value do
      true -> put_change(changeset, :unconfirmed_email, new_value)
      false -> changeset
    end

    submitted? = !!Ecto.Changeset.get_change(changeset, :submitted_at)

    %{rsvpd: !!participation.rsvpd}

You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/KKqjMbXoIJA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B7iTRj%3Do62adGM%3DspKxnscAT1QoVC-0uqLDS_YPcDGAA%40mail.gmail.com.

Andrea Leopardi

unread,
Dec 23, 2020, 10:42:21 AM12/23/20
to elixir-lang-core
What's the particular advantage of "to_boolean(term)" over "not is_nil(term)"? That it supports also booleans, which however could be used directly in predicates?

Andrea

Moxley Stratton

unread,
Dec 23, 2020, 11:13:44 AM12/23/20
to elixir-l...@googlegroups.com
"not is_nil(term)" breaks up an expression to make it more difficult to read:

Example 1:

    case not is_nil(membership_ends_on) or not is_nil(trial_ends_on) or dues_exempt do

versus

    case to_boolean(membership_ends_on || trial_ends_on || dues_exempt) do

Example 2:

    submitted? = not is_nil(Ecto.Changeset.get_change(changeset, :submitted_at))

versus

    changeset
    |> Ecto.Changeset.get_change(:submitted_at)
    |> to_boolean()

This expression couldn't use "not is_nil(expr)":

    %{rsvpd: !!participation.rsvpd}

That is because "participation.rsvpd" is either nil or a boolean.

Bruce Tate

unread,
Dec 23, 2020, 1:04:22 PM12/23/20
to elixir-l...@googlegroups.com
I support this feature. The arguments for 

  • a solution that composes well with pipes and 
  • the linters rejecting the most common solution of !!

are all particularly good ones. From my perspective, I teach composition with pipes in the core, `with` in the boundary, and I try to have students return a boolean rather than a truthy result as part of good API design. 

The inability to convert from truthy to boolean has always felt like a missing feature to me. 

-bt



--

Regards,
Bruce Tate
CEO

José Valim

unread,
Dec 23, 2020, 1:39:53 PM12/23/20
to elixir-l...@googlegroups.com
Thank you for the examples.

However, I believe those examples are not really checking for a boolean, but rather for nil. If you don't want to use is_nil, you can always compare with nil, which is also pipeable:

    def has_access? do
      at_least_logged_in? and get_csrf_cookie(session) != nil
    end

    %{event_form | submitted: event_form.submitted_at != nil}

    case membership_ends_on || trial_ends_on || dues_exempt do
      nil -> add_error(changeset, :membership_ends_on, "is required")
      _ -> changeset
    end


Moxley Stratton

unread,
Dec 23, 2020, 1:50:25 PM12/23/20
to elixir-l...@googlegroups.com
José:

I agree on your first example.

Your second example wouldn't work:

    case membership_ends_on || trial_ends_on || dues_exempt do
      nil -> add_error(changeset, :membership_ends_on, "is required")
      _ -> changeset
    end

It wouldn't work because `dues_exempt` is a boolean. If `dues_excempt` is `false`, it wouldn't match the `nil` clause.

José Valim

unread,
Dec 23, 2020, 2:01:31 PM12/23/20
to elixir-l...@googlegroups.com
I see. You can match on both or use a with in this case:

  if membership_ends_on || trial_ends_on || dues_exempt? do
    changeset
  else
     add_error(changeset, :membership_ends_on, "is required")
  end

Austin Ziegler

unread,
Dec 23, 2020, 3:03:05 PM12/23/20
to elixir-l...@googlegroups.com
It would be easy enough to provide this as a predicate in your own code if that’s your style preference

```elixir
def boolean(falsy) when falsy in [nil, false], do: false
def boolean(_), do; true

# OR
def boolean(test), do: if(test, do: true, else: false)

# OR
def boolean(test), do: !!test
```

It can read a little more intentional to say `%{x | assigned?: boolean(assigned_to_id)}` than `%{x | assigned?: !!assigned_to_id}` or `%{x | assigned?: !is_nil(assigned_to_id)}`, but I don’t really think this adds much value to the core.

-a





--

eksperimental

unread,
Dec 25, 2020, 11:48:53 PM12/25/20
to elixir-l...@googlegroups.com
This will work:

defguard to_boolean(term) when term not in [false, nil]
> >>>> - a solution that composes well with pipes and
> >>>> - the linters rejecting the most common solution of !!
> >>>>>>>>> *José Valim*
> >>>>>>>>> www.plataformatec.com.br
> >>>>>>>>> Skype: jv.ptec
> >>>>>>>>> Founder and Director of R&D
> >>>>>>>>>
> >>>>>>>> --
> >>>>>>>> You received this message because you are subscribed to the
> >>>>>>>> Google Groups "elixir-lang-core" group.
> >>>>>>>> To unsubscribe from this group and stop receiving emails
> >>>>>>>> from it, send an email to
> >>>>>>>> elixir-lang-co...@googlegroups.com. To view this
> >>>>>>>> discussion on the web visit
> >>>>>>>> https://groups.google.com/d/msgid/elixir-lang-core/45dc9a7b-1657-4d02-933b-fe8fe182c2bcn%40googlegroups.com
> >>>>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/45dc9a7b-1657-4d02-933b-fe8fe182c2bcn%40googlegroups.com?utm_medium=email&utm_source=footer>
> >>>>>>>> .
> >>>>>>>>
> >>>>>>> --
> >>>>>>> You received this message because you are subscribed to a
> >>>>>>> topic in the Google Groups "elixir-lang-core" group.
> >>>>>>> To unsubscribe from this topic, visit
> >>>>>>> https://groups.google.com/d/topic/elixir-lang-core/KKqjMbXoIJA/unsubscribe
> >>>>>>> .
> >>>>>>> To unsubscribe from this group and all its topics, send an
> >>>>>>> email to elixir-lang-co...@googlegroups.com.
> >>>>>>> To view this discussion on the web visit
> >>>>>>> https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B7iTRj%3Do62adGM%3DspKxnscAT1QoVC-0uqLDS_YPcDGAA%40mail.gmail.com
> >>>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B7iTRj%3Do62adGM%3DspKxnscAT1QoVC-0uqLDS_YPcDGAA%40mail.gmail.com?utm_medium=email&utm_source=footer>
> >>>>>>> .
> >>>>>>>
> >>>>>> --
> >>>>>> You received this message because you are subscribed to the
> >>>>>> Google Groups "elixir-lang-core" group.
> >>>>>> To unsubscribe from this group and stop receiving emails from
> >>>>>> it, send an email to
> >>>>>> elixir-lang-co...@googlegroups.com. To view this
> >>>>>> discussion on the web visit
> >>>>>> https://groups.google.com/d/msgid/elixir-lang-core/CAFJDP5fJWCVCU41R0CQWx-q6RD1ENUtw92bcpSunf6SBNfQtsg%40mail.gmail.com
> >>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/CAFJDP5fJWCVCU41R0CQWx-q6RD1ENUtw92bcpSunf6SBNfQtsg%40mail.gmail.com?utm_medium=email&utm_source=footer>
> >>>>>> .
> >>>>>>
> >>>>> --
> >>>>> You received this message because you are subscribed to the
> >>>>> Google Groups "elixir-lang-core" group.
> >>>>> To unsubscribe from this group and stop receiving emails from
> >>>>> it, send an email to
> >>>>> elixir-lang-co...@googlegroups.com. To view this
> >>>>> discussion on the web visit
> >>>>> https://groups.google.com/d/msgid/elixir-lang-core/CAM9Rf%2BLMgPAwYN8FyEs0t%3DVvmAQzP9gx-VjykT6Q11Dvd%3DsVyA%40mail.gmail.com
> >>>>> <https://groups.google.com/d/msgid/elixir-lang-core/CAM9Rf%2BLMgPAwYN8FyEs0t%3DVvmAQzP9gx-VjykT6Q11Dvd%3DsVyA%40mail.gmail.com?utm_medium=email&utm_source=footer>
> >>>>> .
> >>>>>
> >>>>
> >>>>
> >>>> --
> >>>>
> >>>> Regards,
> >>>> Bruce Tate
> >>>> CEO
> >>>>
> >>>>
> >>>> <https://bowtie.mailbutler.io/tracking/hit/f8218219-d2a8-4de4-9fef-1cdde6e723f6/c7c97460-016e-45fb-a4ab-0a70318c7b97>
> >>>>
> >>>> Groxio, LLC.
> >>>> 512.799.9366
> >>>> br...@grox.io
> >>>> grox.io
> >>>>
> >>>> --
> >>>> You received this message because you are subscribed to the
> >>>> Google Groups "elixir-lang-core" group.
> >>>> To unsubscribe from this group and stop receiving emails from
> >>>> it, send an email to
> >>>> elixir-lang-co...@googlegroups.com. To view this
> >>>> discussion on the web visit
> >>>> https://groups.google.com/d/msgid/elixir-lang-core/CAFXvW-47q8nXAKw%2B8ZH_NHh2booBmEYtDf-dB70GHXPDXnZmXw%40mail.gmail.com
> >>>> <https://groups.google.com/d/msgid/elixir-lang-core/CAFXvW-47q8nXAKw%2B8ZH_NHh2booBmEYtDf-dB70GHXPDXnZmXw%40mail.gmail.com?utm_medium=email&utm_source=footer>
> >>>> .
> >>>>
> >>> --
> >>> You received this message because you are subscribed to a topic
> >>> in the Google Groups "elixir-lang-core" group.
> >>> To unsubscribe from this topic, visit
> >>> https://groups.google.com/d/topic/elixir-lang-core/KKqjMbXoIJA/unsubscribe
> >>> .
> >>> To unsubscribe from this group and all its topics, send an email
> >>> to elixir-lang-co...@googlegroups.com.
> >>> To view this discussion on the web visit
> >>> https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4J5P%2BdmuOB94xq1L9poz7t8_sjQMub3nYGwrzEYYHgqAQ%40mail.gmail.com
> >>> <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4J5P%2BdmuOB94xq1L9poz7t8_sjQMub3nYGwrzEYYHgqAQ%40mail.gmail.com?utm_medium=email&utm_source=footer>
> >>> .
> >>>
> >> --
> >> You received this message because you are subscribed to the Google
> >> Groups "elixir-lang-core" group.
> >> To unsubscribe from this group and stop receiving emails from it,
> >> send an email to elixir-lang-co...@googlegroups.com.
> >> To view this discussion on the web visit
> >> https://groups.google.com/d/msgid/elixir-lang-core/CAFJDP5fA2A6Au1vWXVWB8WamCU8prEzUDwR22VXW7%2B%3D6xp7xqg%40mail.gmail.com
> >> <https://groups.google.com/d/msgid/elixir-lang-core/CAFJDP5fA2A6Au1vWXVWB8WamCU8prEzUDwR22VXW7%2B%3D6xp7xqg%40mail.gmail.com?utm_medium=email&utm_source=footer>
> >> .
> >>
> > --
> > You received this message because you are subscribed to the Google
> > Groups "elixir-lang-core" group.
> > To unsubscribe from this group and stop receiving emails from it,
> > send an email to elixir-lang-co...@googlegroups.com.
> > To view this discussion on the web visit
> > https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B0rS0Qb%3D1UG6Qa-MC1J8CndBXWkwbbs0kgftq8j_YeaQ%40mail.gmail.com
> > <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B0rS0Qb%3D1UG6Qa-MC1J8CndBXWkwbbs0kgftq8j_YeaQ%40mail.gmail.com?utm_medium=email&utm_source=footer>
> > .
> >
>
>

Reply all
Reply to author
Forward
0 new messages