# Using has\_type(list(Type), X) with user created type not working as expected

**URL:** <https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340>\
**Category:** Help!\
**Created:** [October 1, 2019, 5:38pm UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340 "2019-10-01T17:38:25Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [October 1, 2019, 5:38pm UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/1 "2019-10-01T17:38:25Z")

</div>

When using [has\_type/2](https://www.swi-prolog.org/pldoc/man?predicate=has_type/2) added more types via [multifile](https://www.swi-prolog.org/pldoc/man?predicate=multifile/1) which are working fine.

Then wanted to use

```prolog
has_type(list(Type), X) :-
    is_list(X), element_types(X, Type).

```

by calling `has_type/2` from the module importing the library errors, and the code started failing.

As near as I can figure, `element_types/2` ([GitHub](https://github.com/SWI-Prolog/swipl-devel/blob/e2998ce1b44e2a75e3fc7020f805d4533b071a3c/library/error.pl#L387)) needs to be exported.

* * *

> **Example**
>
> ```prolog
> 
> :- module(example, []).
> 
> :- use_module(library(error)).
> 
> % :- use_module(library(error),
> % [
> % element_types/2
> % ]).
> 
> :- multifile
> error:has_type/2.
> 
> error:has_type(key_value,Key_value) :-
> key_value_validate(Key_value).
> 
> key_value_validate(Key_value) :-
> must_be(ground,Key_value),
> Key_value = kv(Key,Value),
> must_be(ground,Key),
> must_be(ground,Value).
> 
> :- begin_tests(example_bad).
> 
> test(1) :-
> has_type(list(key_value),[kv(1,a),kv(2,b)]).
> 
> :- end_tests(example_bad).
> 
> ```
> 
> ```prolog
> has_type_workaround(list(Type), X) :-
> is_list(X), error:element_types(X, Type).
> 
> :- begin_tests(example_workaround).
> 
> test(2) :-
> has_type_workaround(list(key_value),[kv(1,a),kv(2,b)]).
> 
> :- end_tests(example_workaround).
> 
> ```
> 
> ```prolog
> ?- run_tests.
> % PL-Unit: example_bad 
> ERROR: d:/has_type_list_example.pl:28:
> test 1: received error: plunit_example_bad:'unit body'/2: Unknown procedure: plunit_example_bad:has_type/2
> done
> % PL-Unit: example_workaround . done
> % 1 test failed
> % 1 tests passed
> false.
> 
> ```

* * *

Also tried using

```prolog
:- use_module(library(error),
    [
        element_types/2
    ]).

```

instead of

```prolog
:- use_module(library(error)).

```

which resulted in

```prolog
?- consult('D:/has_type_list_example.pl').
Warning: d:/has_type_list_example.pl:9:
Warning: import/1: error:element_types/2 is not exported (still imported into example)
true.

```

Can [use\_module/2](https://www.swi-prolog.org/pldoc/doc_for?object=use_module/2) be used to import predicates from modules that did not export the predicate? This has me confused.

---

<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:** [October 2, 2019, 8:47am UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/2 "2019-10-02T08:47:39Z")

</div>

> [@EricGT](#):
>
> Can [use\_module/2](https://www.swi-prolog.org/pldoc/doc_for?object=use_module/2) be used to import predicates from modules that did not export the predicate?

With a warning, yes. You should avoid warnings though 🙂 Otherwise it is not clear to me what you are trying to do. I see no reason for element\_types/2 to be exported. Can you share the code you want to write?

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [October 2, 2019, 9:30am UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/3 "2019-10-02T09:30:24Z")

</div>

> [@jan](#):
>
> Can you share the code you want to write?

Yes I can, it will be large but let me ask this first. 😃

I did make a complete minimal working example in the previous post, did you click on the triangle

![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/1X/c991e95cac4525b3597e3282579adf49017cb185.png)  
to expand the section

![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/1X/14173ff488e5439072735e2ca5dcd49384acede4.png)  
…

to see the code example?

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [October 2, 2019, 9:41am UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/4 "2019-10-02T09:41:44Z")

</div>

Note on the example code. This clause:

> [@EricGT](#):
>
> ```prolog
> key_value_validate(Key_value) :-
> must_be(ground,Key_value),
> Key_value = kv(Key,Value),
> must_be(ground,Key),
> must_be(ground,Value).
> 
> ```

can be simplified to:

```prolog
key_value_validate(Key_value) :-
    must_be(ground,Key_value),
    Key_value = kv(_,_).

```

If `Key_value` is ground, then necessarily `Key` and `Value` are also ground.

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [October 2, 2019, 10:43am UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/6 "2019-10-02T10:43:59Z")

</div>

3 posts were split to a new topic: [Types with SWI-Prolog](https://swi-prolog.discourse.group/t/types-with-swi-prolog/1341)

---

<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:** [October 2, 2019, 10:00am UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/7 "2019-10-02T10:00:47Z")

</div>

The issue seems to be

```prolog
:- begin_tests(example_bad).

test(1) :-
    has_type(list(key_value),[kv(1,a),kv(2,b)]).

:- end_tests(example_bad).

```

error:has\_type/2 is multifile, but not part of the interface of library(error). Type checking is done using must\_be/2 (throws error) or is\_of\_type/2 (fails)

```prolog
:- begin_tests(example_bad).

test(1) :-
    is_of_type(list(key_value),[kv(1,a),kv(2,b)]).

:- end_tests(example_bad).

```

With that change I get

```prolog
101 ?- run_tests.
% PL-Unit: example_bad . done
% test passed
true.

```

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [October 2, 2019, 10:31am UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/10 "2019-10-02T10:31:23Z")

</div>

Follow up for others interested in using library errors.

I have been using [library errors](https://www.swi-prolog.org/pldoc/man?section=error) in creating some deterministic code (emulator) and found that there are two very different ways of using the type checking.

1. [is\_of\_type/2](https://www.swi-prolog.org/pldoc/doc_for?object=is_of_type/2) fails and is useful with non-deterministic code.

2. [must\_be/2](https://www.swi-prolog.org/pldoc/doc_for?object=must_be/2) throws exceptions and is useful with deterministic code when the condition should not occur. I use this to stop the execution of the code immediately in test cases.

So from the [answer](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/7) by Jan learned that instead of using [has\_type/2](https://www.swi-prolog.org/pldoc/doc/_SWI_/library/error.pl) which is not exported, should use either `is_of_type/2` or `must_be/2`.

* * *

> **Example**
>
> ```prolog
> :- module(example, []).
> 
> :- use_module(library(error)).
> 
> :- multifile
> error:has_type/2.
> 
> error:has_type(key_value,Key_value) :-
> key_value_validate(Key_value).
> 
> key_value_validate(Key_value) :-
> is_of_type(ground,Key_value),
> Key_value = kv(_,_).
> 
> :- begin_tests(valid_examples).
> 
> test(3) :-
> is_of_type(list(key_value),[kv(1,a),kv(2,b)]).
> 
> test(4,[fail]) :-
> is_of_type(list(key_value),[kv(1,_),kv(2,b)]).
> 
> test(5) :-
> must_be(list(key_value),[kv(1,a),kv(2,b)]).
> 
> test(6,[error(type_error(key_value,kv(1,_)),_)]) :-
> must_be(list(key_value),[kv(1,_),kv(2,b)]).
> 
> :- end_tests(valid_examples).
> 
> ```
> 
> Running
> 
> ```prolog
> ?- run_tests.
> % PL-Unit: valid_examples .... done
> % All 4 tests passed
> true.
> 
> ```

* * *

And from the [answer](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/4) by Paulo that if you test a term for ground then all of the arguments must also be ground, so no need to test each one.

Also must note that the list of types as seen in [must\_be/2](https://www.swi-prolog.org/pldoc/doc_for?object=must_be/2) is not a complete list. To see all of the types, you have to look at the source code on GitHub for [has\_type/2](https://github.com/SWI-Prolog/swipl-devel/blob/e2998ce1b44e2a75e3fc7020f805d4533b071a3c/library/error.pl#L332)

* * *

In creating the test cases for this example to force one to return `fail` and one to return `error` found out that for any user created predicate of `error:has_type/2` it should not use `must_be/2` but `is_of_type/2` because if the code needs to fail and calls `has_type/2` with a `must_be/2` it will not fail but cause an error, however when `must_be/2` calls a user created predicate of `error:has_type/2` and `error:has_type/2` fails, then `must_be/2` can return an error.

* * *

If you also read my other post, you know that I like [algebraic data types](https://en.wikipedia.org/wiki/Algebraic_data_types) and use to think they could not be done with SWI-Prolog until I found package [typedef](https://www.swi-prolog.org/pack/list?p=typedef). I have not used it yet, but worth noting for others looking at using types with SWI-Prolog.

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [October 2, 2019, 10:44am UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/11 "2019-10-02T10:44:50Z")

</div>

> [@EricGT](#):
>
> If you also read my other post, you know that I like [algebraic data types](https://en.wikipedia.org/wiki/Algebraic_data_types) and use to think they could not be done with SWI-Prolog until I found package [typedef](https://www.swi-prolog.org/pack/list?p=typedef). I have not used it yet, but worth noting for others looking at using types with SWI-Prolog.

Doesn’t parametric types gives you in practice the same expressive power of algebraic data types? The parameters on a parametric type can be (and usually are) other types, which can also be parametric. With the basic parametric types defined by default, you can define quite complex types.

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [October 2, 2019, 11:19am UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/12 "2019-10-02T11:19:53Z")

</div>

> [@pmoura](#):
>
> Doesn’t parametric types gives you in practice the same expressive power of algebraic data types?

I should know that, but I don’t.

As such I will take some time to learn more about this and possibly add some links.

> **tl;dr**
>
> Parametric types makes be think of [generics](https://en.wikipedia.org/wiki/Generic_programming) in other languages, and algebraic data types make me think of [discriminated unions](https://en.wikipedia.org/wiki/Tagged_union), and to me the two are different. Not saying you are wrong, but I can’t say you are right.
> 
> One of these years I will actually take the time to work through “Types and Programming Languages” by [Benjamin C. Pierce](http://www.cis.upenn.edu/~bcpierce) ([link](https://www.cis.upenn.edu/~bcpierce/tapl/)) ([WoldCat](http://www.worldcat.org/oclc/1077359413))
> 
> In just asking you made me realize that I did not mention one of the reasons I like algebraic types.
> 
> While they let you compose types from other types, during the compilation phases of [type inference](https://en.wikipedia.org/wiki/Type_inference) and [checking](https://en.wikipedia.org/wiki/Type_system#Type_checking), it can also identify where the code does not cover all of the cases of the type. I wanted to give an example, but finding examples of bad code with error messages is not easy.
> 
> In other words, when I think of algebraic data types, I think of the data as a set, and the algebraic data type definition defines what is in the set. In languages such as F# when you write a [switch statement](https://en.wikipedia.org/wiki/Switch_statement) to process the data type, the compiler will warn you if the switch statement did not cover all of the items in the set. This then lets me think of the data with [function compositions](https://en.wikipedia.org/wiki/Function_composition) like this
> 
> ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/1X/39d27108eb3fdda8a5f8ae205797b4a373c3bbc8.png)
> 
> By [Stephan Kulla](http://kulla.me) ([User:Stephan Kulla](https://commons.wikimedia.org/wiki/User:Stephan_Kulla "User:Stephan Kulla")) - Own work, [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0 "Creative Commons Attribution-Share Alike 4.0"), [Link](https://commons.wikimedia.org/w/index.php?curid=44553625)
> 
> and because of the warnings about not covering the complete set, it gets me closer to thinking of the code as a proof which means the codes is more correct.
> 
> When I program in F# (which has been a few years ago because I haven’t needed it lately) I could go for weeks without ever needing the debugger by just learning to effectively make use of the type system and fix all of the warnings. In programming Prolog, I miss the warnings about the cases not covered and only discovered during run time.

---

<div class="post-metadata">

**Author:** ![swi](https://avatars.discourse-cdn.com/v4/letter/s/51bf81/32.png) [@swi](https://swi-prolog.discourse.group/u/swi)\
**Post date:** [October 7, 2019, 5:10am UTC](https://swi-prolog.discourse.group/t/using-has-type-list-type-x-with-user-created-type-not-working-as-expected/1340/13 "2019-10-07T05:10:16Z")

</div>

> [@EricGT](#):
>
> If you also read my other post, you know that I like [algebraic data types](https://en.wikipedia.org/wiki/Algebraic_data_types) and use to think they could not be done with SWI-Prolog until I found package [typedef](https://www.swi-prolog.org/pack/list?p=typedef).

You can also use [attributed variables](https://www.swi-prolog.org/pldoc/man?section=attvar) to make algebraic data types. The example given in the link is pretty close.

You can use [conditional compilation](https://www.swi-prolog.org/pldoc/man?section=conditionalcompilation) to turn it on if you want it only during development.
