10

In Prolog predicates, I often write repetitive conditional statements like this one, but I wish they could be written more concisely:

output(Lang, Type, Output) :-   
    (Lang = javascript ->
        Output = ["function", Type];
    Lang = ruby ->
        Output = ["def", Type];
    Lang = java ->
        Output = [Type]).

Would it be possible to replace this series of conditional statements with a more concise switch-statement?

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Anderson Green
  • 30,230
  • 67
  • 195
  • 328
  • 1
    The parentheses you have written around the conditions are redundant. You should instead place a single pair of parentheses around the whole (a->b;c->d;e) construct. This is best practice to avoid surprises when you want to conjoin the conditional with other goals. – jschimpf Mar 04 '16 at 16:05
  • @jschimpf I fixed the problem, so it seems more concise now. – Anderson Green Mar 05 '16 at 01:21

3 Answers3

10

In Prolog it is quite easy to define your own control structures, using meta-predicates (predicates that take goals or predicates as arguments).

For example, you could implement a switch construct like

switch(X, [
    a : writeln(case1),
    b : writeln(case2),
    c : writeln(case3)
])

by defining

switch(X, [Val:Goal|Cases]) :-
    ( X=Val ->
        call(Goal)
    ;
        switch(X, Cases)
    ).

If necessary, this can then be made more efficient by compile-time transformation as supported by many Prolog systems (inline/2 in ECLiPSe, or goal expansion in several other systems).

And via operator declarations you can tweak the syntax to pretty much anything you like.

jschimpf
  • 4,904
  • 11
  • 24
7

It seems that multiple clauses are made for this use case and also quite concise.

output(javascript, Type, ["javascript", Type]).
output(ruby, Type, ["def", Type]).
output(java, Type, [Type]).
coredump
  • 37,664
  • 5
  • 43
  • 77
  • Of course, it will only be concise if the predicate has a small number of arguments. I'm still searching for a more concise way to implement switch statements in Prolog. – Anderson Green Mar 04 '16 at 04:37
  • For sure, multiple clauses are not made for this use case. They are **the main** control flow tool available in Prolog. As such, of course they cover this banal use case. – CapelliC Jun 18 '16 at 06:55
1

slightly shorter:

output(Lang, Type, Output) :-   
    (Lang, Output) = (javascript, ["function", Type]) ;
    (Lang, Output) = (ruby, ["def", Type]) ;
    (Lang, Output) = (java, [Type]).

idiomatic:

output(Lang, Type, Output) :-
  memberchk(Lang-Output, [
    javascript - ["function", Type],
    ruby - ["def", Type],
    java - [Type]
  ]).
CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • 2
    Using memberchk/2 is a rather inefficient way on structure copying Prolog implementations (which are AFAIK all currently actively maintained implementations) as it first copies the entire structure to the stack, then runs memberchk/2 and finally leaves recollection of the copied structure to GC. The first is more acceptable, but using separate clauses as the next answe is the real Prolog way: consise and fast due to clause indexing. – Jan Wielemaker Mar 03 '16 at 20:03
  • @JanWielemaker: I agree, my answer is not good... Also, my first snippet is not equivalent to OP' code. but neither separate clauses would be, without cuts... – CapelliC Mar 03 '16 at 20:09