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)))
(or (index-of lst1 sym) (index-of lst2 sym))
Another possibility:
(cond
[(index-of lst1 sym)]
[else (index-of lst2 sym)])
The first one is more “idiomatic”
Yeah, I wouldn’t really recommend the second one for this particular problem :wink:
a case for resyntax?
Sometimes I wish or
is not a control flow operator
for what cases?
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.
Just to be clear, I’m not against an operator that has or
semantics. I just wish it’s not named or
.
ah, so what you want is for or
to actually produce a boolean?
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.
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.
Agree, in fact I feel that or
is more like search (I often use for/or
to search for a value).
or first-not-false
or or-else
, idk
With a doubly mutable version or-else!!
for naughty booleans
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
I had forgotten about that aspect of cond
, and the test-expr => proc-expr
one also, so thanks!
The Scheme/Racket’s or
is not just logic or
, it is something like a Maybe Alternative or MonadPlus with Left Catch in Haskell.
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.
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)`.
But whether that is beautiful, or, the sort of thing @sorawee wants to minimize…?
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:
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:
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
.
@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)
Totally agree the latter is not great.
(I wonder how often that arises from punning #f
to mean “failure”, as opposed to using an option type or raising an exception.)
(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.)
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.
what if foo
is number->string
?
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.