# Must\_be, can\_be, is\_not

**URL:** <https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535>\
**Category:** General\
**Created:** [May 9, 2023, 4:35pm UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535 "2023-05-09T16:35:22Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![z5h](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/z5h/32/5445_2.png) [@z5h](https://swi-prolog.discourse.group/u/z5h)\
**Post date:** [May 9, 2023, 4:35pm UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/1 "2023-05-09T16:35:22Z")

</div>

Scryer Prolog has a `can_be` predicate: [scryer-prolog/error.pl at c1218fc98688a397080ec41d6ba57ef5e70a05e1 · mthom/scryer-prolog · GitHub](https://github.com/mthom/scryer-prolog/blob/c1218fc98688a397080ec41d6ba57ef5e70a05e1/src/lib/error.pl#L7)

Looking for similar in SWI-Prolog, I see `is_not` but it appears to be private. [swipl-devel/error.pl at 7bcfc8546b9acf3b966b1d80d36d4cecb086721b · SWI-Prolog/swipl-devel · GitHub](https://github.com/SWI-Prolog/swipl-devel/blob/7bcfc8546b9acf3b966b1d80d36d4cecb086721b/library/error.pl#L266).

Is there a reason not to expose it?

If it was exposed, could I do something like this:  
`can_be(Type, Term) :- \+ is_not(Type, Term).`

---

<div class="post-metadata">

**Author:** ![brebs](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@brebs](https://swi-prolog.discourse.group/u/brebs)\
**Post date:** [May 9, 2023, 5:34pm UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/2 "2023-05-09T17:34:21Z")

</div>

I’m not sure about `can_be`’s purpose, but [code](https://github.com/dtonhofer/prolog_notes/blob/45f15e13a093c7ea8da5f90f6af662592f7bd891/swipl_notes/about_type_tests/unit_tests_for_must_be.pl#L130) is linked to at must\_be/2 - is it helpful?

---

<div class="post-metadata">

**Author:** ![z5h](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/z5h/32/5445_2.png) [@z5h](https://swi-prolog.discourse.group/u/z5h)\
**Post date:** [May 9, 2023, 5:37pm UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/3 "2023-05-09T17:37:41Z")

</div>

`must_be` guarantees a term is a particular type. The purpose of `can_be` is to say that as it stands, a term can later be unified to type.

The notes in the example you linked to say:

> The corresponding predicates do not exist. We add an implmentation for “atom” for illustration.

I’m suggesting: the corresponding predicates might easily exist or be implemented. IF we had access to `is_not`.

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [May 9, 2023, 7:19pm UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/4 "2023-05-09T19:19:50Z")

</div>

> [@z5h](#):
>
> The purpose of `can_be` is to say that as it stands, a term can later be unified to type.

You can use freeze/2:

```prolog
?- freeze(X, must_be(integer, X)), X = 1.
X = 1.
?- must_be(integer, X).
ERROR: Arguments are not sufficiently instantiated
   ...

```

---

<div class="post-metadata">

**Author:** ![z5h](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/z5h/32/5445_2.png) [@z5h](https://swi-prolog.discourse.group/u/z5h)\
**Post date:** [May 9, 2023, 8:14pm UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/5 "2023-05-09T20:14:49Z")

</div>

Good to know about freeze, thanks. But still not at useful as `can_be` for this purpose. For example, this will cause problems (even though `L` can be a list):

```prolog
freeze(L, must_be(list, L)), L = [1|T], T = [].

```

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [May 9, 2023, 8:21pm UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/6 "2023-05-09T20:21:59Z")

</div>

> [@z5h](#):
>
> `freeze(L, must_be(list, L)), L = [1|T], T = [].`

Yes, there ought to be “freeze” versions of the various must\_be/2 predicates, and `proper_list` would need to be implemented differently than `freeze(L, must_be(proper_list, L))`.

(If `can_be/2` does nothing when the variable is uninstantiated, then it’s rather misleading.)

---

<div class="post-metadata">

**Author:** ![z5h](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/z5h/32/5445_2.png) [@z5h](https://swi-prolog.discourse.group/u/z5h)\
**Post date:** [May 9, 2023, 9:12pm UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/7 "2023-05-09T21:12:15Z")

</div>

> [@peter.ludemann](#):
>
> If `can_be/2` does nothing when the variable is uninstantiated, then it’s rather misleading.

How so? If I want a predicate to test that a term can be unified with a list, what should I call it? Whatever the answer, let’s call it that.

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [May 10, 2023, 6:38am UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/8 "2023-05-10T06:38:32Z")

</div>

> [@z5h](#):
>
> > [@peter.ludemann](#):
> >
> > If `can_be/2` does nothing when the variable is uninstantiated, then it’s rather misleading.
> 
> How so? If I want a predicate to test that a term can be unified with a list, what should I call it?

What is the meaning of `can_be(list, X), X = an_atom`? If `can_be/2` succeeds if the argument is uninstantiated, then this seems to be logically nonsense (because `X = an_atom, can_be(list, X)` will fail.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [May 10, 2023, 9:04am UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/9 "2023-05-10T09:04:31Z")

</div>

It is definitely not `is_not/2`. That predicate is part of must\_be/2 and responsible for the error message. So, if X is not a list we call `is_not(list, X)` which will figure out which error to throw (basically an instantiation error or a type error).

The idea of a `can_be/2` makes sense. Shouldn’t be too hard to add to library(error). If someone likes to give it a try, I’m happy to consider the PR.

It might better (?) relate to work that is sleeping for a long time and defines Prolog types as constraints. So, you can do `?- type(X, atom), type(Y, integer), X = Y.` and it will fail 🙂 On the other hand, this library is more intended for abstract interpretation/type checking. I assume can\_be/2 is no constraint, so we can happily do this?

```
?- can_be(integer, X), X = a.

```

---

<div class="post-metadata">

**Author:** ![z5h](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/z5h/32/5445_2.png) [@z5h](https://swi-prolog.discourse.group/u/z5h)\
**Post date:** [May 10, 2023, 1:40pm UTC](https://swi-prolog.discourse.group/t/must-be-can-be-is-not/6535/10 "2023-05-10T13:40:29Z")

</div>

> [@jan](#):
>
> It is definitely not `is_not/2`.

Yes, I see that now.

> [@jan](#):
>
> `can_be(integer, X), X = a.`

Indeed, it’s expected to work this way.

In the “Power of Prolog” video I linked, Markus Triska explains it as:

> Silent failure for wrong types is default practice in Prolog code.  
> Type errors help us determine the exact location of mistakes.  
> However, a sound distinction between type errors and instantiation errors is hard to get right manually.  
> Therefore, if you want type errors, use:
> 
> - must\_be/2 for arguments that must be sufficiently instantiated
> - can be/2 for output and general arguments

> [@jan](#):
>
> If someone likes to give it a try, I’m happy to consider the PR.

👍
