mildc055ee
2021-11-1 08:12:54

I wonder that there is any better way to treat the result of test condition like below. (let ((res (do-something))) (if res res (do-fallback))) I want to (do-fallback) if (do-something) is #f, otherwise return the result of (do-something).

for more concrete code: (let ((idx (index-of lst1 sym))) (if idx idx (index-of lst2 sym)))


sorawee
2021-11-1 08:15:40

(or (index-of lst1 sym) (index-of lst2 sym))


sorawee
2021-11-1 08:18:15

Another possibility:

(cond [(index-of lst1 sym)] [else (index-of lst2 sym)])


sorawee
2021-11-1 08:18:34

The first one is more “idiomatic”


laurent.orseau
2021-11-1 08:19:04

Yeah, I wouldn’t really recommend the second one for this particular problem :wink:


laurent.orseau
2021-11-1 08:19:33

a case for resyntax?


sorawee
2021-11-1 08:22:32

Sometimes I wish or is not a control flow operator


laurent.orseau
2021-11-1 08:24:25

for what cases?


sorawee
2021-11-1 08:28:20

Well, actually, let me clarify what I meant by that:

I actually have no problem with or as a control flow operator. The short circuiting behavior is perfectly fine.

But when I read code, at least for most programming languages, I would expect an or expression to produce a boolean.

Using (or rather, abusing) or to produce a non-boolean (that is to be used as a non-boolean value) hinders readability, IMO. It’s clever when you see this trick for the first time, but when it’s used all over the place, I think it creates a headache.


sorawee
2021-11-1 08:30:38

Just to be clear, I’m not against an operator that has or semantics. I just wish it’s not named or.


laurent.orseau
2021-11-1 08:30:52

ah, so what you want is for or to actually produce a boolean?


sorawee
2021-11-1 08:32:11

Not really. Especially if you want operands of or to be in the tail position, I don’t think you can enforce booleanness. And I don’t mind or resulting in a non-boolean, as long as the result is used as if it’s a boolean.


sorawee
2021-11-1 08:32:56

It’s just a social problem. There should be another operator that does what or does, and people should use it instead of abusing or when they want to produce non-boolean.


noahstorym
2021-11-1 08:35:50

Agree, in fact I feel that or is more like search (I often use for/or to search for a value).


laurent.orseau
2021-11-1 08:44:42

or first-not-false


sorawee
2021-11-1 09:58:31

or or-else, idk


laurent.orseau
2021-11-1 10:32:08

With a doubly mutable version or-else!! for naughty booleans


notjack
2021-11-1 12:59:33

I think I’ve got one like that for and already, so if I don’t have one for or it would be a fine addition


badkins
2021-11-1 14:54:50

I had forgotten about that aspect of cond, and the test-expr => proc-expr one also, so thanks!


chansey97
2021-11-1 15:22:17

The Scheme/Racket’s or is not just logic or, it is something like a Maybe Alternative or MonadPlus with Left Catch in Haskell.


soegaard2
2021-11-1 15:24:03

I like, that the short name or is used for the most common operation (short circuiting or). So I am fine with having to write logical-or or boolean-or for the version that returns a boolean.


greg
2021-11-1 15:51:46

I mean (and v #t) is the essence of “boolean-ize v” (make it #t or #f). So you can always wrap that around the or:(and (or ___) #t)`.


greg
2021-11-1 15:51:58

But whether that is beautiful, or, the sort of thing @sorawee wants to minimize…?


greg
2021-11-1 15:52:33

I think this aspect of programming is a “know your audience”. Either is fine if some team prefers it. It probably also helps to be consistent within some unit like a project or file or at least a specific function definition. :slightly_smiling_face:


greg
2021-11-1 15:56:29

Maybe just don’t add a logical-or named oar because it’s clever because “the paddle has two sides”, and otherwise I’ll be fine with anything personally. :stuck_out_tongue:


chansey97
2021-11-1 16:45:40

Combining and and or seems very useful, because it can be seen as a path through a tree. (and (or (and (goal1) (or (goal2) (goal3)) (goal4)) (and (or (goal5) (and (goal6) (goal7))) (goal8) (goal9))) (goal10) (goal11)) Similar to miniKanren’s all and conde.


sorawee
2021-11-1 16:56:32

@greg to be clear, I’m perfectly fine with truthy values in Racket that is not #t, as long as it’s used as a boolean.

Say, foo :: number? -> (or/c number? #f). I’m perfectly OK with:

(if (foo 5) 1 2) What I have problem with is something like:

(+ (or (foo 5) 1) 2)


greg
2021-11-1 17:07:08

Totally agree the latter is not great.


greg
2021-11-1 17:11:02

(I wonder how often that arises from punning #f to mean “failure”, as opposed to using an option type or raising an exception.)


greg
2021-11-1 17:13:15

(I’m not saying punning #f to mean “failure” or “N/A” is always bad. It’s a thing many scheme/racket functions do. I’m just saying it seems to “set the stage” for things like the second example, or make it more defensible.)


chansey97
2021-11-1 18:15:49

I tried some other approaches: (with-handlers ([exn:fail:index-of? (lambda (exn) (index-of lst2 sym) )]) (index-of lst1 sym)) Or (define (index-of/kk l s fallback succeed) (cond ((index-of ls s) => succeed) (else (fallback succeed)))) (index-of/kk lst1 sym (λ (sk) (index-of/kk lst2 sym (λ (sk) #f) (λ (v) (sk v)))) (λ (v) v)) Neither of them are ideal… The and and or approach is still a good option, especially when combining and and or to construct a path tree. The root cause is that the use case needs to consider failure paths, but usually everyday programming only considers a happy path and throws all the possible errors to the top-level.


laurent.orseau
2021-11-1 18:20:57

what if foo is number->string?


greg
2021-11-1 20:38:26

Sometimes I use match where #f is one of the match patterns. This feels closer to a pattern match on an option type (where #f plays the role of None). It does nothing to help with #f needing to be a valid, non-“None” value. But at least the intent seems clearer… maybe? Don’t have a strong opinion about this.