`and/or` accept values that are not boolean

For example, we can write down

(and #t 1 0) ; 0
(and 1 #t 0) ; 0
(and 1 0 #t) ; #t
(or 0 #t #f) ; 0
(or #t 0 #f) ; #t
(or #t #f 0) ; #t

I think these are two questions

  1. why they accept non-boolean?
  2. how should I interpret results in the mind model?

https://docs.racket-lang.org/guide/conditionals.html should be able to answer both of your questions.

emmm...yes, but still think it's weird XD.

The key thing here is that and and or are not logical operators.

They are instead "short-circuting" operators, which means they don't evaluate all arguments if the result is clear from the first arguments.

The or operation returns the first "true" (non-false) value.

> (or #f #t (/ 1 0))
#t

The evaluation was "short-circuited" in the sense that (/ 1 0) was never evaluated.

The second thing to keep in mind is that #f is the only false value and all other values count as true.
This variation of the first example shows that #t can be replaced with any non-false value:

> (or #f 42 (/ 1 0))
42

In short:

  • The or operation returns the first "true" (non-false) value.
  • If there are no "true" values, then the result is #f.
  • Arguments after the first "true" are not evaluated.

Similarly:

  • The and operation returns the last "true" value, if all arguments are "true".
  • If at least one argument is false, the result is #f.
  • If a false argument is found, the following arguments are not evaluated.

An common use of and is:

(and <check-some-condition> <evaluate-expression>)

where the expression would result in an error, of the condition isn't met.

See more on short-circuiting here:

One special case of

(and <check-some-condition> <evaluate-expression>)

that I find interesting is using

(and <some-value> #t)

to "convert" any "true" value to #t. In other words, with this expression the result will be either #t or #f (the latter if <some-value> is #f).