# Is there a more elegant way to write this?

**URL:** <https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273>\
**Category:** Help!\
**Created:** [February 13, 2019, 4:18pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273 "2019-02-13T16:18:37Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![sprior](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/sprior/32/108_2.png) [@sprior](https://swi-prolog.discourse.group/u/sprior)\
**Post date:** [February 13, 2019, 4:18pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/1 "2019-02-13T16:18:37Z")

</div>

I’ve got predicates which handle the initialization of a specific device so the declaration of that predicate includes the device ID (happens to be a MAC address). So while the predicate declaration line hardcodes the id it feels very redundant to keep writing out that id in subsequent lines. Is there a better way of coding this?

```
neopixel_init('5c:cf:7f:00:1c:87'):-
   send_neopixel_setspeed('5c:cf:7f:00:1c:87', 5000),
  send_neopixel_setbrightness('5c:cf:7f:00:1c:87',50),
  send_neopixel_mode('5c:cf:7f:00:1c:87', 14),
  send_neopixel_cmd('5c:cf:7f:00:1c:87','start').
```

---

<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:** [February 13, 2019, 4:48pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/2 "2019-02-13T16:48:29Z")

</div>

Not sure what the various objects are, but roughly I’d think about something along these lines:

```
init :-
    neopixel(MacAdr),
    something_init(MacAdr).

something_init(MacAdr) :-
    something_setspeed(MacAdr, 5000),
    something_setbrightness(MacAdr,50),
    somethingo_mode(MacAdr, 14),
    something_cmd(MacAdr,'start').

neopixel('5c:cf:7f:00:1c:87').
```

---

<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:** [February 13, 2019, 4:48pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/3 "2019-02-13T16:48:37Z")

</div>

How about this:

```auto
neopixel_device_id('5c:cf:7f:00:1c:87').

neopixel_init(DeviceId):-
  send_neopixel_setspeed(DeviceId, 5000),
  send_neopixel_setbrightness(DeviceId, 50),
  send_neopixel_mode(DeviceId, 14),
  send_neopixel_cmd(DeviceId, 'start').

```

and invoke it by: `neopixel_device_id(DeviceId), neopixel_init(DeviceId)`.

---

<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:** [February 13, 2019, 4:49pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/4 "2019-02-13T16:49:21Z")

</div>

Seems we agree quite closely 🙂

---

<div class="post-metadata">

**Author:** ![sprior](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/sprior/32/108_2.png) [@sprior](https://swi-prolog.discourse.group/u/sprior)\
**Post date:** [February 13, 2019, 4:59pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/5 "2019-02-13T16:59:42Z")

</div>

I might have misstated the requirement. That list of neopixel initialization instructions is specific to that particular device id, the instructions for a different id would be completely different and I’d create a different predicate for them with their device id as the matching value. I’ve used the approach in the replies in other places, but it’s not what I’m going for here.

---

<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:** [February 13, 2019, 5:02pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/6 "2019-02-13T17:02:52Z")

</div>

> [@jan](#):
>
> Seems we agree quite closely 🙂

Only when I’m correct. 😉

---

<div class="post-metadata">

**Author:** ![sprior](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/sprior/32/108_2.png) [@sprior](https://swi-prolog.discourse.group/u/sprior)\
**Post date:** [February 13, 2019, 5:07pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/7 "2019-02-13T17:07:36Z")

</div>

A more complete view of what I’m trying to do is here. The entry point is the message predicate which gets called as a result of an incoming MQTT message and the first parameter is a list of the topic segments and the second is the actual message body (not used here). So I’m trying to code the unique initialization requirements of that specific device.

```
message(['Smarthome','Notify', 'neopixel', 'start', NeopixelId],_):-
   neopixel_init(NeopixelId).

neopixel_init('cc:50:e3:17:6a:1e'):-
    send_neopixel_setbrightness('cc:50:e3:17:6a:1e',50),
    send_neopixel_setsegment('cc:50:e3:17:6a:1e',0,0,0,11,0xffff00,8000,'F'),
    send_neopixel_setsegment('cc:50:e3:17:6a:1e',1,1,8,42,0x0000ff,1000,'F'),
    send_neopixel_setsegment('cc:50:e3:17:6a:1e',2,9,20,42,0x00ffff,1500,'T'),
    send_neopixel_setsegment('cc:50:e3:17:6a:1e',3,21,36,42,0x00ff00,2000,'F'),
    send_neopixel_setsegment('cc:50:e3:17:6a:1e',4,37,60,42,0xffff00,4000,'T'),
    send_neopixel_setsegment('cc:50:e3:17:6a:1e',5,61,92,42,0xff0000,6000,'F'),
    send_neopixel_cmd('cc:50:e3:17:6a:1e','start').

neopixel_init('5c:cf:7f:00:1c:87'):-
   send_neopixel_setspeed('5c:cf:7f:00:1c:87', 5000),
   send_neopixel_setbrightness('5c:cf:7f:00:1c:87',50),
   send_neopixel_mode('5c:cf:7f:00:1c:87', 14),
   send_neopixel_cmd('5c:cf:7f:00:1c:87','start').
```

---

<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:** [February 13, 2019, 5:29pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/8 "2019-02-13T17:29:27Z")

</div>

```auto
neopixel_init(DeviceId) :- 
   DeviceId = '5c:cf:7f:00:1c:87',
   !,
   send_neopixel_setspeed(DeviceId, 5000),
   send_neopixel_setbrightness(DeviceId, 50),
   send_neopixel_mode(DeviceId, 14),
   send_neopixel_cmd(DeviceId,'start').

```

?

This has the disadvantage that there’s no argument indexing, and it needs the ugly cut to avoid a choicepoint, but performance probably isn’t an issue for you during initialization unless you have thousands of these devices.

---

<div class="post-metadata">

**Author:** ![sprior](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/sprior/32/108_2.png) [@sprior](https://swi-prolog.discourse.group/u/sprior)\
**Post date:** [February 13, 2019, 5:44pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/9 "2019-02-13T17:44:49Z")

</div>

OK thanks. I guess I wasn’t missing anything obvious.  
BTW, the initialization for ‘cc:50:e3:17:6a:1e’ is the code for the animated Christmas tree I built:

 ![IMG_20190127_123401](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/1X/94fb0d5539dcd40059506eb0499e5e04609a115f.jpeg)

---

<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:** [February 13, 2019, 6:28pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/10 "2019-02-13T18:28:33Z")

</div>

The loss of clause indexing for this code is a missing feature of SWI-Prolog. In the end, the original code is the most efficient, just not comfortable to write. The trick with Prolog is then to invent any cute term you really would like to write and a rule for term\_expansion/2 to generate the efficient code. That could be something like

`init('5c:cf:7f:00:1c:87', [set_speed(5000), setbrightness(50), mode(14), cmd(start)]).`

Note that you generally can also interpret the above fact. For your purposes that is probably efficient enough.

---

<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:** [February 13, 2019, 6:34pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/11 "2019-02-13T18:34:16Z")

</div>

```auto
neopixel_init('5c:cf:7f:00:1c:87') :- 
    maplist(
        cmd( '5c:cf:7f:00:1c:87'),
        [send_neopixel_setspeed(5000),
         send_neopixel_setbrightness(50),
         send_neopixel_mode(14),
         send_neopixel_cmd('start')]).

cmd(DeviceId, CmdAndArgs) :-
    CmdAndArgs =.. [Cmd|Args],
    Call =.. [Cmd,DeviceId|Args],
    Call.

```

---

<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:** [February 13, 2019, 6:43pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/12 "2019-02-13T18:43:41Z")

</div>

Jan and I almost agree, but Jan’s answer is better. 😁

---

<div class="post-metadata">

**Author:** ![sprior](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/sprior/32/108_2.png) [@sprior](https://swi-prolog.discourse.group/u/sprior)\
**Post date:** [February 13, 2019, 6:43pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/13 "2019-02-13T18:43:47Z")

</div>

The funny thing is that we’re kinda going in circles because what those send\_neopixel\* predicates actually do is to create a Prolog dictionary and then convert that to JSON which is the payload of the MQTT message:

```
send_neopixel_setspeed(NeoPixelId, S):-
   send_neopixel_cmd(NeoPixelId,"setSpeed",_{s:S}).

send_neopixel_cmd(NeoPixelId, Function, Args):-
   atom_json_dict(Json, [_{f:Function, args:Args}] ,[]),
   publish(['Smarthome','neopixel',NeoPixelId], Json).
```

---

<div class="post-metadata">

**Author:** ![sprior](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/sprior/32/108_2.png) [@sprior](https://swi-prolog.discourse.group/u/sprior)\
**Post date:** [February 13, 2019, 6:49pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/14 "2019-02-13T18:49:20Z")

</div>

The code for my LCD and OLED displays is similar, but combines multiple operations into one message like this:

```
display_oled_vehicle_json(Json, Name, Timestamp):-
   atom_json_dict(Json,[
                     _{f:"clear"},
                     _{f:"setTextAlignment", args:_{textAlignment:"CENTER"}},
                     _{f:"setColor", args:_{color:"WHITE"}},
                     _{f:"setFont", args:_{font:"ArialMT_Plain_16"}},
                     _{f:"drawString", args:_{x:64, y:0, text:"Recognized"}},
                     _{f:"drawString", args:_{x:64, y:20, text:Name}},
                     _{f:"drawString", args:_{x:64, y:40, text:Timestamp}},
                     _{f:"display"},
                     _{f:"setBlankTime", args:_{seconds:18000}}
                   ],[]).

```

This makes this possible:

 ![IMG_20190126_204540](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/1X/58d0f0b52c320c313aa0b68d7638989845ceea92.jpeg)

---

<div class="post-metadata">

**Author:** ![barb](https://avatars.discourse-cdn.com/v4/letter/b/58956e/32.png) [@barb](https://swi-prolog.discourse.group/u/barb)\
**Post date:** [February 13, 2019, 8:27pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/15 "2019-02-13T20:27:16Z")

</div>

How about replacing each of the 5 ‘…’ constants with the same variable?

(Also, I don’t understand why you have such a constant in the head pattern.)

---

<div class="post-metadata">

**Author:** ![sprior](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/sprior/32/108_2.png) [@sprior](https://swi-prolog.discourse.group/u/sprior)\
**Post date:** [February 13, 2019, 8:40pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/16 "2019-02-13T20:40:32Z")

</div>

@barb, I think your question is addressed in the reply above that starts " A more complete view of what I’m trying to do is here."

---

<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:** [February 14, 2019, 9:11am UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/17 "2019-02-14T09:11:42Z")

</div>

Cute! The overall way of thinking in Prolog is to first model your domain using Prolog terms in such a way that it feels comfortable and neat. That is, without worrying about how to process all this. So, you describe your devices and their properties such as their name, mac address, etc. You typically organise them in classes, so you can describe common properties if you have more (almost) identical devices.

Only after that you start worrying how you send the messages around and make it all work. In many cases you can just write predicates that use the data. That may be slow (for IoT rarely an issue), in which case you compile the data to an efficient program. If that too is too slow you compile it to a C program and link that, etc.

---

<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:** [February 14, 2019, 10:49am UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/18 "2019-02-14T10:49:12Z")

</div>

It is not clear to me if all devices are unique or if there are also several instances of the same device. But in both cases, I usually start by defining a protocol for the devices interface. In this case, one of the main predicates would be `init/0`:

```nohighlight
:- protocol(device).

    :- public(init/0).

:- end_protocol.

```

If a device is unique, it can be represented by a prototype that implements the `device` protocol. For example:

```nohighlight
:- object('5c:cf:7f:00:1c:87', implements(device)).

    init :-
        this(MAC), % this goal is inlined
        neopixel::(
            setspeed(MAC, 5000),
            setbrightness(MAC, 50),
            mode(MAC, 14),
            cmd(MAC, 'start')
        ).

:- end_object.

```

Assuming that all `neopixel` public predicates take a MAC address would allow us to simplify to:

```nohighlight
:- object('5c:cf:7f:00:1c:87', implements(device)).

    init :-
        this(MAC), % this goal is inlined
        neopixel(MAC)::(
            setspeed(5000),
            setbrightness(50),
            mode(14),
            cmd('start')
        ).

:- end_object.

```

If there are several instances of the same device, let’s say, `lamp`, that only differ on the parameters, I would use instead a parametric object:

```nohighlight
:- object(lamp(_MAC_,_Speed_,_Brightness_,_Mode_,_Cmd_), implements(device)).

    init :-
        neopixel(_MAC_)::(
            setspeed(_Speed_),
            setbrightness(_Brightness_),
            mode(_Mode_),
            cmd(_Cmd_)
        ).

:- end_object.

```

And then define a Prolog table with facts (parametric object proxies) for each lamp:

```nohighlight
% lamp(Mac, Speed, Brightness, Mode, Cmd)
lamp('5c:cf:7f:00:1c:87', 5000, 50, 14, 'start').
lamp('cc:50:e3:17:6a:1e', 7500, 35, 42, 'start').
...

```

I can then e.g. initialize all lamps by calling:

```nohighlight
?- {lamp(_,_,_,_,_)}::init, fail; true.

```

Alternatively:

```nohighlight
?- forall(lamp(MAC,S,B,M,C), lamp(MAC,S,B,M,C)::init).

```

We can also initialize just a specific device given its MAC address:

```nohighlight
?- {lamp('5c:cf:7f:00:1c:87',_,_,_,_)}::init.

```

If multiple devices share all but a few of the parameterizations, we can also due for example:

```nohighlight
:- object(lamp_mode(MAC, Mode), extends(lamp(MAC,5000,50,Mode,'start')).

:- end_object.

lamp_mode('5c:cf:7f:00:1c:87', 14).
lamp_mode('cc:50:e3:17:6a:1e', 42).
...

```

By now, you can see there are several common points between this structured solution and other suggestions on this discussion thread. Some of the advantages of this structured approach is that it easily scales to a large number of devices where we can have a mix of unique devices and multiple instances of the same devices with just different parameterizations and taxonomies of devices where features are inherited.

---

<div class="post-metadata">

**Author:** ![barb](https://avatars.discourse-cdn.com/v4/letter/b/58956e/32.png) [@barb](https://swi-prolog.discourse.group/u/barb)\
**Post date:** [February 14, 2019, 11:55pm UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/19 "2019-02-14T23:55:51Z")

</div>

How about replacing each of the 5 ‘…’ constants with the same variable?

(Also, I don’t understand why you have such a constant in the head pattern.)

---

<div class="post-metadata">

**Author:** ![CapelliC](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/capellic/32/17_2.png) [@CapelliC](https://swi-prolog.discourse.group/u/CapelliC)\
**Post date:** [April 25, 2019, 10:33am UTC](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273/20 "2019-04-25T10:33:48Z")

</div>

I would try to compress problem syntax, just a ‘primary key’ packing of code, from left to right:

```prolog
neopixel_ID('5c:cf:7f:00:1c:87').
neopixel_init(ID):- neopixel_ID(ID), maplist({ID}/[F=V]>>call(F,ID,V),[
  send_neopixel_setspeed=5000,
  send_neopixel_setbrightness=50,
  send_neopixel_mode=14,
  send_neopixel_cmd='start']).

```

maplist compresses a lot the statement, common arguments are abstracted away.  
And a bit more:

```prolog
neopixel_init(ID):- neopixel_ID(ID), maplist({ID}/[F=V]>>(
   atom_concat('send_neopixel_',F,G),call(G,ID,V)
 ),[
  setspeed=5000,
  setbrightness=50,
  mode=14,
  cmd='start']).

```

Or, up to point on the request:

```prolog
neopixel_init('5c:cf:7f:00:1c:87'):- maplist([F=V]>>(
   atom_concat(send_neopixel_,F,G),
   ( is_list(V) -> As = V ; As = [V] ),
   C=..[G,'5c:cf:7f:00:1c:87'|As],
   call(C)
 ),[
  setspeed=5000,
  setbrightness=50,
  mode=14,
  setsegment=[1,1,8,42,0x0000ff,1000,'F'],
  cmd=start]).

```

To debug, simply wrap last call in writeln, like this

```prolog
   C=..[G,'5c:cf:7f:00:1c:87'|As],
   writeln(call(C))

```

and ‘run’ it:

```prolog
?- uty:neopixel_init('5c:cf:7f:00:1c:87').
call(send_neopixel_setspeed(5c:cf:7f:00:1c:87,5000))
call(send_neopixel_setbrightness(5c:cf:7f:00:1c:87,50))
call(send_neopixel_mode(5c:cf:7f:00:1c:87,14))
call(send_neopixel_setsegment(5c:cf:7f:00:1c:87,1,1,8,42,255,1000,F))
call(send_neopixel_cmd(5c:cf:7f:00:1c:87,start))

```

Make reusable, just a bit of cleanup to generalize:

```prolog
neopixel_init('5c:cf:7f:00:1c:87'):-
    maplist(pfix_call_addr_args(send_neopixel_,'5c:cf:7f:00:1c:87'),
           [setspeed=5000
           ,setbrightness=50
           ,mode=14
           ,setsegment=[1,1,8,42,0x0000ff,1000,'F']
           ,cmd=start]).

```

the uty itself:

```prolog
:- module(uty,
          [mapdict/2
          ,pfix_call_addr_args/3
          ]).

% use only if keys order is unimportant
mapdict(F,D) :-
    dict_pairs(D,Tag,Ps), % Tag is useless here
    maplist({F,Tag}/[K-V]>>call(F,Tag,K,V), Ps).

% generalize and make simpler moving here all args business
pfix_call_addr_args(Pfix,Addr,F=V) :-
    atom_concat(Pfix,F,G),
    ( is_list(V) -> As = V ; As = [V] ),
    C=..[G,Addr|As],
    writeln(call(C)).

```

Ciao, Carlo

[Next page](https://swi-prolog.discourse.group/t/is-there-a-more-elegant-way-to-write-this/273.md?page=2)
