trueagi-io / metta-examples

Discussion of MeTTa programming with examples
MIT License
14 stars 15 forks source link

Case as replacement for cond #37

Open DaddyWesker opened 4 months ago

DaddyWesker commented 4 months ago

I've got some question regarding possibility to replace scheme's cond with metta's case. Is it possible? I've tried to write simple check function to see if my idea works:

(= (check $x $y)
    (case $x
        ((0 1)
        ((> $x $y) $x)
        ($_ $y))))

But apparently it doesn't work since, as i understand case, it will check not if $x larger than $y but instead if (== $x (> $x $y)) (expression equality). Is there some way to write this code using case in current metta state? Or only consequent if's are usable in that case? @vsbogd @Necr0x0Der

vsbogd commented 4 months ago

Yes, case doesn't reduce conditions, but with current implementation you can calculate the result of the condition in advance and then compare it with expected result. For instance following will work:

(= (check $x $y)
  (case ($x (> $x $y)) (
    ((0 $_) 1)
    (($_ True) $x)
    ($_ $y)
  )))
DaddyWesker commented 4 months ago

Well, question was for general case and example was just to demonstrate my question. In general It could be needed to check several different conditions. Anyway, thanks for the answer.

vsbogd commented 4 months ago

In general It could be needed to check several different conditions.

I agree this approach is too verbose for many different conditions. You can also right your own case implementation:

(: my-case (-> %Undefined% Expression %Undefined%))
(= (my-case $x $conds)
  (if (== $conds ())
    (empty)
    (let ($cond $templ) (car-atom $conds)
      (let $tail (cdr-atom $conds)
        (if $cond $templ (my-case $x $tail)) ))))

(= (check $x $y)
  (my-case $x (
    ((== $x 0)  1)
    ((> $x $y) $x)
    (True $y)
  )))

But pay attention that check also should be modified. In implementation from description unification pattern and boolean conditions are mixed in a list of conditions. To simplify implementation one need to choose either using unification (then use library case) or conditioning (then use my-case).

Necr0x0Der commented 4 months ago

Yes, case doesn't reduce conditions and works more like case in functional languages, otherwise something like this would work

(= (check $x $y)
    (case True
        (((== 0 $x) 1)
         ((> $x $y) $x)
         ($_ $y))))

but it doesn't. This difference should be explained in a tutorial.

Necr0x0Der commented 4 months ago

Another funny was would be to write:

(= (check $x $y)
   (let $conds
        (((== 0 $x) 1)
         ((> $x $y) $x)
         ($_ $y))
       (case True $conds))
)

It seemingly works, but the difference with normal case is that all the branches will be evaluated. It's not a tragedy for simple cases, but ruins the idea sequential mutually-exclusive evaluation in case .

Necr0x0Der commented 4 months ago

BTW, my guess is that in @vsbogd implementation (let $tail (cdr-atom $conds) will also evaluate the whole tail. It may need a more careful consideration, but my first guess is that this

(= (my-case $x $conds)
  (if (== $conds ())
    (empty)
    (let ($cond $templ) (car-atom $conds)
      (if $cond
          $templ
          (let $tail (cdr-atom $conds) (my-case $x $tail)) ))))

will at least screen out evaluation of the tail if $cond is true. Unfortunately, $templ will be evaluated even if $cond is false.

vsbogd commented 4 months ago

(let $tail (cdr-atom $conds) will also evaluate the whole tail.

Yes, unfortunately it is inevitable with current let semantics. It can be eliminated in minimal MeTTa using chain and eval wisely though.