chansey97
2020-7-15 15:31:57

I’d like to continue discussing this thread. https://racket.slack.com/archives/C09L257PY/p1591877598012800

Why not use undefined as initial value for those fields. https://docs.racket-lang.org/reference/undefined.html

> The constant undefined can be used as a placeholder value for a value to be installed later, especially for cases where premature access of the value is either difficult or impossible to detect or prevent. Since @dbriemann mentioned that “he has a class with fields …”, so I suppose he was writing object-oriented code, while object-oriented code usually means imperative style code.

In imperative style code, we use mutable data structures frequently and there are some situations where we can not know the value of some fields in advance. (e.g. cross-reference, etc)

In these situations, we need a null-pointer or nullable-type, instead of Bool. So I think undefined is a good option.

What do you think about it?


soegaard2
2020-7-15 15:57:48

Usually undefined is reserved for the compiler to emit. For example letrec can bind variables to undefined - and if they are referenced before they are defined, it is an error.


samth
2020-7-15 15:59:58

That undefined is distinct from unsafe-undefined which is what letrec uses, and which you should not use (except in very special cases)


chansey97
2020-7-15 16:00:08

@soegaard2 > letrec can bind variables to undefined imo, that is cross-reference.


samth
2020-7-15 16:00:19

The undefined in racket/undefined is not used anywhere that I know of.


chansey97
2020-7-15 16:02:02

So when you need cross-reference-like stuff, you should use undefined.


chansey97
2020-7-15 16:02:55

I said cross-reference-like stuff means: > there are some situations where we can not know the value of some fields in advance. You need a placeholder value.


samth
2020-7-15 16:04:13

(Actually, that’s not quite right, it’s used by shared, by class contracts in some forms, by the syntax/toplevel library, and in a few other random places.)


sorawee
2020-7-15 16:05:16

@chansey97: the protocol that Racket programs usually use is, if #f is not a value of interest, then use #f for the placeholder value.


soegaard2
2020-7-15 16:05:17

In other words: It’s used to implement binding constructs where an expression may refer to the variable before it is initialized.


sorawee
2020-7-15 16:06:58

If #f is a value of interest, then you have a couple of options


chansey97
2020-7-15 16:07:03

@sorawee I don’t think use #f (which is a Bool) as placeholder is good practice. Imagine if a value happens to be #f


sorawee
2020-7-15 16:07:42

I don’t think it is either, but that’s the idiom that Racket programs have been using since forever.



samth
2020-7-15 16:10:47

you can use ~a instead of number->string


soegaard2
2020-7-15 16:10:56

(for*/sum ([i 3001] [c (~a i)] #:when (eqv? c #\2)) 1)


sorawee
2020-7-15 16:12:44

@chansey97 If you have a strong opinion that this idiom is not good, you can say so at https://github.com/racket/rhombus-brainstorming. Rhombus is a project for what the “next version of Racket” would be.

But Racket, as it is right now, has this idiom everywhere, and it would be difficult to break the idiom in the way that maintains consistency.


sorawee
2020-7-15 16:14:37

And several people attempted to fix this idiom. See https://docs.racket-lang.org/rebellion/Option_Values.html for example. But it also means that you must always use these libraries when you write programs. You can’t write vanilla Racket anymore.


chansey97
2020-7-15 16:19:48

@sorawee Thanks for telling me this website. I will submit a proposal later.


badkins
2020-7-15 16:44:49

I agree that using #f to represent a missing value isn’t ideal, but with minor tweaking, it can work surprisingly well in practice, at least it has for me. Since I know I’ll likely be using this idiom, I try and define my booleans in such a way that a default value of #f is reasonable. Rails treats null values in the database as false for booleans, and I never had an issue with that.


badkins
2020-7-15 17:14:39

@wanpeebaw clearly the winner is (string-length (string-replace (string-join (map ~a (sequence->list (in-range 3001))) "") #px"[^2]+" ""))


badkins
2020-7-15 17:21:32

On a more serious note, count seems like a reasonable function to have, so once you have that: (for/sum ([i 3001]) (count (~a i) #\2))


badkins
2020-7-15 17:34:03

(define (count str ch) (for/sum ([ c str ]) (if (eqv? c ch) 1 0)))


badkins
2020-7-15 17:37:03

Hmm… it looks like my original loop is faster than for (define (count str c) (let ([ len (string-length str) ]) (let loop ([i 0][ sum 0 ]) (if (< i len) (loop (add1 i) (if (eqv? c (string-ref str i)) (add1 sum) sum)) sum))))


samdphillips
2020-7-15 18:24:38

Maybe use in-string in sequence form?


badkins
2020-7-15 18:28:58

@samdphillips yes, that definitely helps. Not enough, but it does help :slightly_smiling_face: It’s not surprising when I look at the macro expansion of for/sum - it’s doing a lot.


samdphillips
2020-7-15 19:03:59

The expansion looks pretty similar and the for loop is using unsafe ops so it does seem non-intuitive why the there is a difference.


samdphillips
2020-7-15 19:05:13

(they look similar up to the point that I stopped the expansion that is, maybe the inner for loop that the for generates does something wonky)


samdphillips
2020-7-15 19:11:52

Things in the generated for loop like (if #t xxx yyy) and (if (and #t (not #f)) xxx yyy) seem suspicious


badkins
2020-7-15 19:14:01

I forgot about unsafe ops, I added unsafe-string-length, unsafe-char=? and unsafe-string-ref, and that made mine 1.7% faster :slightly_smiling_face: Probably not worth it.


badkins
2020-7-15 19:14:42

I ran 7 benchmarks, deleted the best/worst two and averaged the remaining 3.


badkins
2020-7-15 19:16:10

I’ve typically found the for family introduces some inefficiency, but it’s usually small, and the resulting code w/ for can look nicer.


samdphillips
2020-7-15 19:29:51

Other idle optimization: using bytes instead of strings (if you can work in latin–1) takes 64% of the time of using strings (both for loops)