# Named constants?

**URL:** <https://swi-prolog.discourse.group/t/named-constants/2289>\
**Category:** Help!\
**Created:** [May 6, 2020, 3:23pm UTC](https://swi-prolog.discourse.group/t/named-constants/2289 "2020-05-06T15:23:32Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![stuz5000](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stuz5000/32/940_2.png) [@stuz5000](https://swi-prolog.discourse.group/u/stuz5000)\
**Post date:** [May 6, 2020, 3:23pm UTC](https://swi-prolog.discourse.group/t/named-constants/2289/1 "2020-05-06T15:23:32Z")

</div>

Am I right in thinking that Prolog(s) don’t support named constants?

To define:

```
 icd10hypertension = R03.0

```

we need to:

```
icd10hypertension('R03.0').

```

?

But to use this, we need to

```
f(A,B,C) :- icd10hypertension(X), patients(A, B, X).

```

suffering runtime unification costs and readability? We would have liked to write:

```
f(A,B,C) :- patients(A, B, icd10hypertension).
```

---

<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:** [May 6, 2020, 3:50pm UTC](https://swi-prolog.discourse.group/t/named-constants/2289/2 "2020-05-06T15:50:53Z")

</div>

Have you looked at using [Conditional compilation and program transformation](https://www.swi-prolog.org/pldoc/man?section=progtransform)?

---

<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 6, 2020, 4:19pm UTC](https://swi-prolog.discourse.group/t/named-constants/2289/3 "2020-05-06T16:19:00Z")

</div>

> [@stuz5000](#):
>
> suffering runtime unification costs and readability

The runtime costs are trivial. And I’m not convinced that it’s a significant hit to readability.

Anyway, you could do something like this (you’d probably want better predicate names):

```prolog
f(A, B) :- patients_expand(A, B, icd10hypertension).

patients_expand(A, B, X_exp) :-
    call(X_exp, X),
    patients(A, B, X).

icd10hypertension('R03.0').

```

(I didn’t know what the “C” in your `f(A,B,C)` was for, so I left it out.)

If this turns out to be a performance problem (be sure to profile first), it’s easy to expand `patients_expand/3` at compile-time.

---

<div class="post-metadata">

**Author:** ![bwat](https://avatars.discourse-cdn.com/v4/letter/b/b5a626/32.png) [@bwat](https://swi-prolog.discourse.group/u/bwat)\
**Post date:** [May 6, 2020, 6:13pm UTC](https://swi-prolog.discourse.group/t/named-constants/2289/4 "2020-05-06T18:13:51Z")

</div>

> [@stuz5000](#):
>
> We would have liked to write:
> 
> ```
> f(A,B,C) :- patients(A, B, icd10hypertension).
> 
> ```

When you think about it all constants are named. The constants themselves don’t exist (ok they have an ideal existence!), we use their names. The constant `42` is not a number, it’s a numeral that is used to name a number.

Now, both `icd10hypertension`and `'R02.0'` are ‘constants’. It’s just two different names for the same thing. You just write your code to accept either or both.

---

<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 6, 2020, 6:33pm UTC](https://swi-prolog.discourse.group/t/named-constants/2289/5 "2020-05-06T18:33:33Z")

</div>

> [@stuz5000](#):
>
> f(A,B,C) :- patients(A, B, icd10hypertension).

You can then define a translation table, for example (although it might make more sense to split out the ‘icd10’ part into a 3rd column, giving the code (icd10, snomed, cpt4, etc.); or have multiple code tables.

```prolog
code(icd10_hypertension, 'R02.0').
code(icd10_prehypertension, 'R03.0'). 
code(icd10_essential_hypertension, 'I10').

```

---

<div class="post-metadata">

**Author:** ![stuz5000](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stuz5000/32/940_2.png) [@stuz5000](https://swi-prolog.discourse.group/u/stuz5000)\
**Post date:** [May 6, 2020, 8:01pm UTC](https://swi-prolog.discourse.group/t/named-constants/2289/6 "2020-05-06T20:01:40Z")

</div>

Thanks. Is helpful.

---

<div class="post-metadata">

**Author:** ![stuz5000](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stuz5000/32/940_2.png) [@stuz5000](https://swi-prolog.discourse.group/u/stuz5000)\
**Post date:** [May 7, 2020, 11:16pm UTC](https://swi-prolog.discourse.group/t/named-constants/2289/7 "2020-05-07T23:16:24Z")

</div>

Many thanks.

Hmmm… If I have 30 or 40 of:

```prolog
icd10hypertension = R03.0
icd10heart_attack = ...

```

can I export them all from a module without listing each one?

---

<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 7, 2020, 11:54pm UTC](https://swi-prolog.discourse.group/t/named-constants/2289/8 "2020-05-07T23:54:07Z")

</div>

> [@stuz5000](#):
>
> can I export them all from a module without listing each one?

You mean like this?

```prolog
:- module(constants, [code/2]).

code(icd10hypertension, 'R03.0').
code(icd10heart_attack, '...').

```

The individual lines are “facts” that define the predicate `code/2`, so you’re exporting a single predicate with a number of facts.

Of perhaps I misunderstood your question.

---

<div class="post-metadata">

**Author:** ![stuz5000](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stuz5000/32/940_2.png) [@stuz5000](https://swi-prolog.discourse.group/u/stuz5000)\
**Post date:** [May 8, 2020, 12:33am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/9 "2020-05-08T00:33:23Z")

</div>

No, the purpose of named constants (generally) is:

- to have a single place in your code to bind a name to value
- allow the single name to be referenced in many places
- allow the name to be used in place of the value (providing the reader the meaning to the value), and,
- (most importantly), complain if the name does not exist (e.g. mispelled).

If I place this name in a single exported predicate, prolog will fail silently on misspellings or missing codes:

```prolog
  code(icd10hypertension, 'R03.0').
  % f(X)--->false, but should be an error. icd10hypertons is not a missing predicate
  f(X) :- code(icd10hypertons, C), f(icd10hypertension, 'R03.0').

```

right? So,

```prolog
icd10hypertension('R03.0').
% icd10hypertons is a missing predicate
f(X) :- icd10hypertons(C), f(icd10hypertension, 'R03.0').
...

```

is preferred. However, now if I create ‘code.pl’ to define and share these named constants, I need to export each of 50, right? (A PITA, and is easy to miss some).

I don’t define all 20,000 icd10 codes this way – I would get them from a database – just this small number have special meaning in the prolog program and I don’t want to paste in meaningless-to-the-reader icd10 values at those places, or have the program continue without error due to silly spelling mistakes.

---

<div class="post-metadata">

**Author:** ![jamesnvc](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jamesnvc/32/14_2.png) [@jamesnvc](https://swi-prolog.discourse.group/u/jamesnvc)\
**Post date:** [May 8, 2020, 12:37am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/10 "2020-05-08T00:37:24Z")

</div>

You could also use `term_expansion/2` to generate the definitions. Untested, but something like

```prolog
term_expansion(constant(Code, Val), [CodeVal, :- export(Code/1)]) :-
   CodeVal =.. [Code, Val].

% define like
constant(icd10hypertension, 'R03.0').

% in other module
:- use_module(constants, [icd10hypertension/1]).

f(X) :- icd10hypertension(C), g(C).

```

EDIT: Oops, that should be `term_expansion/2`; updated code to work.

---

<div class="post-metadata">

**Author:** ![stuz5000](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stuz5000/32/940_2.png) [@stuz5000](https://swi-prolog.discourse.group/u/stuz5000)\
**Post date:** [May 8, 2020, 12:45am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/11 "2020-05-08T00:45:48Z")

</div>

Hmmm… interesting.  
if I queried the database for the list of predicates, would it find icd10hypertension, or does this method magic it into existence during the query?  
If the latter. Perhaps would be better to query constant during the module’s initialization and assert those names?

---

<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 8, 2020, 12:46am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/12 "2020-05-08T00:46:08Z")

</div>

> [@stuz5000](#):
>
> complain if the name does not exist

If your lookup is one-way, you can do this:

```prolog
code(icd10hypertension, 'R03.0') :- !.
code(icd10heart_attack, '...') :- !.
code(Unknown, _) :- throw(error(unknown_code(Unknown), _)),

```

---

<div class="post-metadata">

**Author:** ![stuz5000](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stuz5000/32/940_2.png) [@stuz5000](https://swi-prolog.discourse.group/u/stuz5000)\
**Post date:** [May 8, 2020, 1:05am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/13 "2020-05-08T01:05:01Z")

</div>

@peter.ludemann Thank you. This would thwart any static error analysis. I’m not sure this makes a difference in Prolog-land though, since it seems to be without many compile-time types. :☹

It also means more visual noise (therefore less readable) than one predicate per code.

We must:

```prolog
 code(icd10hypertension, C), g(C).

```

instead of

```prolog
 icd10hypertension(C), g(C).

```

I was kinda hoping it would be possible to reduce it to:

```prolog
g(icd10hypertension.value).

```

where `predicate.value` is a macro that rewrites `outer(predicate.value)` as `predicate(X), outer_predicate(predicate) `, or somesuch. (ie. or some similar syntax). Does prolog have the notion of unaries-- names bound to exactly one value (in which case the perhaps the compiler could be coerced into simply re-writing the name with its single value)?

---

<div class="post-metadata">

**Author:** ![jamesnvc](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jamesnvc/32/14_2.png) [@jamesnvc](https://swi-prolog.discourse.group/u/jamesnvc)\
**Post date:** [May 8, 2020, 2:05am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/14 "2020-05-08T02:05:47Z")

</div>

> [@stuz5000](#):
>
> if I queried the database for the list of predicates, would it find icd10hypertension, or does this method magic it into existence during the query?

Yes, that predicate would be defined. `term_expansion/2` is essentially macros for Prolog; loading the file would re-write the `constants(whatever, 'R0.3).` line to be as if you had written `whatever('R0.3'). :- export(whatever/1).`

---

<div class="post-metadata">

**Author:** ![stuz5000](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stuz5000/32/940_2.png) [@stuz5000](https://swi-prolog.discourse.group/u/stuz5000)\
**Post date:** [May 8, 2020, 2:59am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/15 "2020-05-08T02:59:47Z")

</div>

Nifty. So its evaluated while the file is compiled? Like a directive? (The docs says "When defined by the user all terms read during consulting are given to this predicate…, ‘consulting’==on load\_file?)

---

<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 8, 2020, 3:56am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/16 "2020-05-08T03:56:24Z")

</div>

Does this do what you want?

```prolog
:- module(codes, [code/2, code_/2]).

% Lookup by Name or Icd10; throw domain_error if not found
% (Does not backtrack of results - for that, use code_/2).
code(Name, Icd10) :-
    ( code_(Name, Icd10)
    -> true
    ; domain_error(valid_name_or_code, code(Name, Icd10))
    ).

user:term_expansion(codes(Codes), Preds) :-
    maplist(code_expand, Codes, Preds).

code_expand(Name=Icd10, code_(Name, Icd10)).

% code_(Name, Icd9) facts (backtrackable)
codes([
       icd10_hypertension = 'R02.0',
       icd10_prehypertension = 'R03.0',
       icd10_essential_hypertension = 'I10'
      ]).

```

```prolog
?- forall(code_(Name, Icd10), 
          writeln(Name = Icd10)).
icd10_hypertension=R02.0
icd10_prehypertension=R03.0
icd10_essential_hypertension=I10

?- forall(code_(Name, Icd10), format('~q~n', [Name = Icd10])).
icd10_hypertension='R02.0'
icd10_prehypertension='R03.0'
icd10_essential_hypertension='I10'

?- code(icd10_hypertension, Icd10).
Icd10 = 'R02.0'.

?- code_(Name, 'R03.0').
Name = icd10_prehypertension.

?- code(nonsense_name, A).
ERROR: Domain error: `valid_name_or_code' expected, found `code(nonsense_name,_1922)'
ERROR: In:
ERROR: [12] throw(error(domain_error(valid_name_or_code,...),_1972))
ERROR: [9] <user>
ERROR: 
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.

```

---

<div class="post-metadata">

**Author:** ![grossdan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/grossdan/32/23_2.png) [@grossdan](https://swi-prolog.discourse.group/u/grossdan)\
**Post date:** [May 8, 2020, 3:59am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/17 "2020-05-08T03:59:58Z")

</div>

If i understand this correctly, all approaches discussed here suggest having a named constant that is retrieved during runtime – in one way or another – via predicate lookup.

I guess this is always slower than a named constant in compiled languages where the symbol gets replaced during compile time by the declared value.

So, to have “real” named constant in prolog would require some kind of extension – perhaps an extension of the macro facility.

Dan

---

<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 8, 2020, 7:05am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/18 "2020-05-08T07:05:14Z")

</div>

> [@grossdan](#):
>
> So, to have “real” named constant in prolog would require some kind of extension – perhaps an extension of the macro facility.

Not so much _extension_. Just use the macro mechanism for replacing some term. The only gotcha is that you somehow have to recognise the thing you want to expand from anything else, so you can give an error if the constant does not exist. You could for example expand `c.name`. The ffi package does something like that to get access to C #define macros

---

<div class="post-metadata">

**Author:** ![bwat](https://avatars.discourse-cdn.com/v4/letter/b/b5a626/32.png) [@bwat](https://swi-prolog.discourse.group/u/bwat)\
**Post date:** [May 8, 2020, 8:15am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/19 "2020-05-08T08:15:35Z")

</div>

> [@stuz5000](#):
>
> @peter.ludemann Thank you. This would thwart any static error analysis. I’m not sure this makes a difference in Prolog-land though, since it seems to be without many compile-time types. :☹

“_We shape our tools, thereafter our tools shape us._” — either W. Churchill or M. McCluhan, or somebody else.

I think you’re fighting the language. You’re either going to:

1. read something similar to Kowalski’s _Logic for Problem Solving_ ( [http://www.doc.ic.ac.uk/~rak/papers/LogicForProblemSolving.pdf](http://www.doc.ic.ac.uk/%7Erak/papers/LogicForProblemSolving.pdf) ), then come back to Prolog, i.e., learn the culturally enforced thought patterns,
2. seek another language that is more acceptable for you,
3. fall back and use something you already have fluency in, or
4. continue as you are suffering much frustration.

I was there with Prolog in the early 1990s (eventually did no. 1). I’m there with another language right now (doing no. 4 I’m afraid, but economics will force me into no. 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:** [May 8, 2020, 11:09am UTC](https://swi-prolog.discourse.group/t/named-constants/2289/20 "2020-05-08T11:09:16Z")

</div>

4 posts were split to a new topic: [C - How is it possible to ensure that code doesn’t just crash due to a uncaught type error and pointers pointing randomly to memory](https://swi-prolog.discourse.group/t/c-how-is-it-possible-to-ensure-that-code-doesn-t-just-crash-due-to-a-uncaught-type-error-and-pointers-pointing-randomly-to-memory/2296)

[Next page](https://swi-prolog.discourse.group/t/named-constants/2289.md?page=2)
