@pihentagy has joined the channel
@oldsin has joined the channel
Having a file with integers in each line (advent of code, day 1), how can I make this lines typed/racket safe?
I guess I should somehow filter out non-integer lines, but this does not help:
Type Checker: Polymorphic function `filter' could not be applied to arguments:
Types: (-> a Any) (Listof a) -> (Listof a)
Arguments: (-> Any Boolean) (Listof (U Complex False))
Expected result: (-> (Listof Integer))
in: (filter integer? (map string->number (file->lines "input1")))
Try defining a line->number
that returns 0 instead of #f
. Then use that with map.
I don’t get it.
what @soegaard2 is saying is that string->number
can return #f
instead of a number
So lines can be a list of numbers or #f
But he filters it with integer?
, so he expects it to work
In fact, it almost works!
There are two problems
First, the type annotation should be (: lines (Listof Integer))
This is because lines
is a list of integers, not a function that produces a list of integers
Second, you need to use exact-integer?
instead of integer?
This is because (integer? 1.0)
returns #t
But 1.0
does not satisfy Integer
Here’s the full code
#lang typed/racket
(: lines (Listof Integer))
(define lines (filter exact-integer?
(map string->number
(file->lines "input1"))))
I think this is the case where the type error message is not ideal
I am surprized why it works. It needs to peek inside the function argument of filter to allow that :open_mouth:
In the Scala world, people use flatMap instead of map when you might have non-values sprinkled in with values in the input. If you have List(Some(2), None, None, Some(4), None, Some(6), None), and you flatMap over it, it ignores the Nones and pulls out 2, 4, 6 and operates on those. @sorawee’s comment above is fine, though.
@pihentagy can you elaborate more on your surprise? Why is it surprising?
And also disappointed that Integer
and integer?
is not in pair
Yeah, I believe it’s that way to maintain backward compatibility
Because string->number
has a result type of (U Complex False)
Right. Typed Racket has a sophisticated type system that supports the “occurrence typing” feature
and this time filter narrows the type
My type definition of lines is wrong and noone has noticed. lines is not a function
@pihentagy :wink:
And I missed to notice my statement was false :slightly_smiling_face:
oooh, my bad
I failed at reading
@soegaard2 Thanks, hadn’t seen that diagram before, or maybe I forgot. Very helpful.
This diagram is from Typed Racket: https://docs.racket-lang.org/ts-reference/type-ref.html#%28part._.Numeric_.Types%29
filter-map should work for this case
(define (one-round g)
[...]
[(symbol=? winner 'TIE)
(one-round (error "blow!")
(make-game (list A B) new-table (add1 (game-round g))))]))
I was at the GNU Emacs REPL (Emacs 27.1, Hendershott’s racket-mode) and I typed (require errortrace). Then I said (watch-game (new-game 2 2)). The output made perfect sense to me: ; blow!
; errortrace...:
; draft.rkt::79914: (error "blow!")
; draft.rkt::79903: (one-round (error "blow!") (make-game (list A B) new-table (add1 (game-round ....))))
; draft.rkt::80003: (watch-game (new-game 2 2))
Then I added (require errortrace) to my draft.rkt and reloaded the REPL DrRacket-style. Now when I do the same thing, I get: ; blow!
; errortrace...:
; draft.rkt::80163: (watch-game (new-game 2 2))
It’s different. I removed (require errortrace) from draft.rkt and reloaded the REPL. Then I said (require errortrace) at the REPL and invoked (watch-game …) again and I got very similar output as the previous. So I can’t seem to reproduce that three-line errortrace that I produced first. I’m puzzled. Why did I see that first stack trace and don’t see it again?
Why does the second output makes less sense to me than the first? It’s not showing the call to (one-round …).
Right—in a world like ML/Haskell, this would not be possible. You’d have a sum-type (e.g., type line = number option
) and you could use something like filter Option.isSome o map Int.fromString
to keep only the valid lines; but the type would always be line list === number option list
. You would have to use List.mapPartial
(à la flat-map) to get number list
. We can prove that all the options are SOME
s, but the type-system cannot. AFAICT, typed-racket can distinguish the cases…
I’m not sure why. If you want to open an issue at https://github.com/greghendershott/racket-mode/issues and include a code example I can try, I’d be happy to take a look.
But also…
Racket Mode has that kind of errortrace “built in”. https://www.racket-mode.com/#racket_002derror_002dcontext
So if that meets your need, great.
Of course it’s possible that Racket Mode’s use of errortrace-lib
is somehow conflicting with your use of it directly. And if so, that may be an issue that I need to address. idk.
But practically it might be moot if using racket-error-context
set to 'high
is all you need, for what you’re doing.
@anything ^
And/or, I think maybe you’re doing the thing it says not to do, here? :) https://docs.racket-lang.org/errortrace/using-errortrace.html
How I would explain it (someone can correct me) is that errortrace takes a program, fully expands it, and rewrites as a somewhat different program.
It can take your program, and rewrite it as one capable of giving better “stack traces”.
It can be used to do things like instrumenting your program for profiling.
And so on.
But your program rewriting itself using errortrace is generally not possible; it’s something that needs to be done to your program, as part of loading and evaluating your program. Which Racket Mode can do, or the instructions at that doc page show another way to do.
Hopefully I’m explaining this well.
As to why it worked, once, from the REPL? The REPL is kind of magical. Sometimes the magic is good and sometimes evil. :slightly_smiling_face:
Racketeers like to say, “the top-level is hopeless”. Sometimes it raises false hope that something will work in a non-REPL setting.
The hoplessness and the false hope are two sides of the same coin, I guess.
I do understand it vaguely — thanks!! (About rewriting my program to be able to provide stack traces.) That makes sense. I must load it before the program is loaded, otherwise the program can’t be rewritten. (I will look into your racket-mode pointers above. Thanks so much. I’ll report anything of relevance later.) Thank you so much.
You’re right, racket-error-context set to ’high is all I really need. Perfect! Thanks again!
@scottclark11 has joined the channel
@scottclark11 has left the channel