mskoh52
2020-8-3 13:29:46

@mskoh52 has joined the channel


pavpanchekha
2020-8-3 15:25:58

Quick Q on the release notes: when it says that CS supports all of the features of BC, that doesn’t mean single-floats were implemented, right?


pavpanchekha
2020-8-3 15:26:13

(Not a problem, it’s not something I really need, but it’s something I’m tracking for Herbie reasons.)



mflatt
2020-8-3 15:38:26

You’re right. In retrospect, “all features” really means “all features that we expect to ever implement”, where single-precision and extended-precision floating-point are the two things not on that list.


mflatt
2020-8-3 15:44:09

@pavpanchekha Racket CS could easily provide an operation that would round a flonum to reflect the value that would fit in a single-precision floating-point number. Would that be useful?


pavpanchekha
2020-8-3 15:57:24

Eh, I wouldn’t bother. The long answer is that Herbie is in the process of making number types “pluggable”. Right now we ship single precision by default but in the future I expect not to (or maybe only enable it by default in BC or something).


pavpanchekha
2020-8-3 15:58:04

To make a plugin, you provide a way to do each fundamental operation to that number type (along with some other things). We already link to libm for everything except basic arithmetic (those aren’t functions so we can’t FFI-bind them directly)


pavpanchekha
2020-8-3 15:58:25

It is not going to be hard to write a little C wrapper for single-precision add, multiple, divide, and subtract!


pavpanchekha
2020-8-3 15:59:09

In principle a double→single function would mean we can implement that without C, by treating singles as a subset of doubles, but why bother?


pavpanchekha
2020-8-3 15:59:23

I’d rather this weird feature live in a Herbie thing than in the Racket platform.


pavpanchekha
2020-8-3 16:00:08

(And of course a shout-out to Brett Saiki and David Thien for doing all the work splitting number types in Herbie.)


mflatt
2020-8-3 16:12:40

Ok!

FWIW, you can already get double->single rounding by using ffi/unsafe to write a _float and read it back, or call a C _float identity function.

A built-on operation would just be 10–100x faster by keeping the conversion in a register. If performance is a concern for the special case of single-precision floats, a pure Racket function that uses fl operations plus a rounding operation would also be faster than calling a C function for arithmetic.


pavpanchekha
2020-8-3 16:13:32

Performance is not much of a concern. Though it surprises many, computing with floating-point numbers is a small percentage of what Herbie does, about 10% by runtime.


pavpanchekha
2020-8-3 16:14:06

(For contrast, computing with bigfloats is 50%)


pavpanchekha
2020-8-3 16:14:21

So even less reason to add this function :slightly_smiling_face:


mflatt
2020-8-3 16:15:34

I’m starting to think I’ll add it, anyway, because it seems useful for some other contexts.


ben
2020-8-3 16:16:00

@samth should Intersection types be appy-able? I expected this program to work, but it says the intersection is not a function type. #lang typed/racket/base (require/typed racket/base (values (Intersection (-> String String) (-> Symbol Symbol)))) (values 'A) Putting an ann around values fixes that problem. But then, the Intersection can’t be turned into a contract.

If there’s no way to get a type like this, then I won’t open an issue


samth
2020-8-3 16:20:25

There’s not much need for that since you can use case->


samth
2020-8-3 16:20:30

But probably it should work


pavpanchekha
2020-8-3 16:22:19

Well, heh, I won’t stop you!


ben
2020-8-3 16:31:41

I got to that program by playing with occurrence type props. … So I think we’re okay now, but will want to fix if there’s a good reason for occurrence typing to add a function type.


ben
2020-8-3 16:32:27

Or maybe, occurrence typing should extend/make a case-> instead of an Intersection


gknauth
2020-8-3 17:50:42

@samth is “Racket BC” “before Chez?” or does it stand for something else?


spdegabrielle
2020-8-3 17:51:32

@gknauth yes


gknauth
2020-8-3 17:51:43

thanks. cute.


samth
2020-8-3 18:10:43

Before Chez, or bytecode


samth
2020-8-3 18:10:58

The double meaning is part of the attraction


mskoh52
2020-8-3 18:30:24

i’m learning racket…is it ok if i post some code for feedback?


mskoh52
2020-8-3 18:31:04

maybe i’ll stick it in the #beginners channel that i just saw


samth
2020-8-3 19:14:52

Yes, you’re welcome to post code in either channel


sorawee
2020-8-4 01:09:42

Can anyone help me understanding

> In case extracting elements from @racket[s] involves a side effect, > they will not be extracted until the first element is extracted from > the resulting stream. in https://docs.racket-lang.org/reference/streams.html?q=stream-tail#%28def._%28%28lib._racket%2Fstream..rkt%29._stream-tail%29%29?


notjack
2020-8-4 01:58:45

@sorawee I think it means that if you have a stream like (stream (explode) 1 2 3) and call stream-tail on it, (explode) won’t be evaluated yet. However once you do something like (stream-first (stream-tail s 1)) then (explode) will be evaluated even though it’s skipped over by stream-tail.


sorawee
2020-8-4 02:00:14

(define xs (stream (println 'a) 2 3)) (stream-first xs) (stream-tail xs 1) a appears only once.


notjack
2020-8-4 04:48:12

yup that’s correct, because each element of a stream is only ever forced at most once


sorawee
2020-8-4 04:50:43

which makes sense, but then we are back at the original question: what does that paragraph mean?


notjack
2020-8-4 05:25:00

I think it’s saying that in this code, 'a also appears once

(stream-first (stream-tail xs 1))

But in this code, it doesn’t appear at all

(define xs (stream (println 'a) 2 3)) (stream-tail xs 1)


sorawee
2020-8-4 05:26:04

But in the first code, 'a doesn’t appear.


sorawee
2020-8-4 05:26:52

I would be very surprised if it does. stream-tail is really a bunch of stream-rest, so it has no business with the first-expr position of stream-cons.


notjack
2020-8-4 05:28:07

huh, it doesn’t? (haven’t tried)


sorawee
2020-8-4 05:28:15

it doesn’t (I’m not sure if I should reply with yep or nope here)


notjack
2020-8-4 05:29:18

neat. I guess it’s just saying that stream-tail doesn’t force any elements at all


notjack
2020-8-4 05:29:40

It’s worded pretty awkwardly


sorawee
2020-8-4 05:29:50

The conclusion I’m drawing is that the doc is needed to be improved lol


notjack
2020-8-4 05:30:38

I wonder if it forces the next-stream expressions


sorawee
2020-8-4 05:30:41

which is what I’m doing right now actually


sorawee
2020-8-4 05:30:58

There’s actually another doc bug in stream-cons


sorawee
2020-8-4 05:31:19

It says that:

> Produces a lazy stream for which stream-first forces the evaluation of first-expr to produce the first element of the stream, and stream-rest forces the evaluation of rest-expr to produce a stream for the rest of the returned stream.


notjack
2020-8-4 05:31:47

like if a stream is made with (stream-cons 1 (print-stuff-and-make-rest-stream)) will stuff be printed when stream-tail is called on it?


sorawee
2020-8-4 05:31:59

Yep, the answer is no :slightly_smiling_face:


sorawee
2020-8-4 05:32:02

(stream-rest (stream-cons 1 (error 'unreached)))


sorawee
2020-8-4 05:32:32

even though the doc says “stream-rest forces the evaluation of rest-expr to produce a stream for the rest of the returned stream.”


notjack
2020-8-4 05:33:36

and I assume if you skip over multiple rest-exprs with stream-tail, they’re still evaluated when stream-first is used on the tail stream, even though their corresponding element expressions aren’t evaluated


sorawee
2020-8-4 05:34:37

Can you reword your reply? I’m not sure if I understand


notjack
2020-8-4 05:34:43

like


notjack
2020-8-4 05:34:55

one sec


notjack
2020-8-4 05:34:56

moving to laptop


notjack
2020-8-4 05:42:01

so I have this program: #lang debug racket (define s (stream-cons #R(values 1) #R(stream-cons #R(values 2) #R(stream-cons #R(values 3) #R(stream 4 5 6))))) (stream-tail s 2)


notjack
2020-8-4 05:42:19

it prints out this when run: (stream-cons (report (values 2)) (report (stream-cons (report (values 3)) (report (stream 4 5 6))))) = #<stream> #<stream>


notjack
2020-8-4 05:42:53

so even though it’s skipping the first two elements, only the first rest-expr is evaluated. the second rest-expr isn’t evaluated yet.


notjack
2020-8-4 05:43:16

(first rest-expr = stream starting with 2, second rest-expr = stream starting with 3)


notjack
2020-8-4 05:44:55

however, if I only skip the first element using (stream-tail s 1), none of the rest-exprs are evaluated at all, which makes me wonder what would happen if I did (stream-tail (stream-tail s 1) 1) instead of (stream-tail s 2)


notjack
2020-8-4 05:45:27

fascinating. (stream-tail (stream-tail s 1) 1) forces the first rest-expr but not the second, just like (stream-tail s 2)


notjack
2020-8-4 05:45:39

yeesh, laziness is complicated


sorawee
2020-8-4 05:45:56

Yeah, all of these are due to the doc bug of stream-cons I mentioned above


sorawee
2020-8-4 05:46:37

In my local racket, I have this:

@defform[(stream-cons first-expr rest-expr)]{ Produces a lazy stream for which @racket[stream-first] forces the evaluation of @racket[first-expr] to produce the first element of the stream, and @racket[stream-rest] produces a lazy stream for which stream operations (@racket[stream-empty?], @racket[stream-first], @racket[stream-rest]) forces the evaluation of @racket[rest-expr] to produce the rest of the returned stream. The first element of the stream as produced by @racket[first-expr] must be a single value. The @racket[rest-expr] must produce a stream when it is evaluated, otherwise the @exnraise[exn:fail:contract?]. After the evaluation of @racket[first-expr] or @racket[rest-expr], the result is memoized so that subsequent access does not recompute the expression. @examples[#:eval sequence-evaluator <elided>] }


notjack
2020-8-4 05:47:32

It’s really, really weird that (stream-rest (stream-cons first-expr rest-expr)) doesn’t force rest-expr but (stream-rest (stream-rest (stream-cons first-expr rest-expr))) does.


notjack
2020-8-4 05:48:02

or at least, weird to me. I don’t understand the use case for that.


sorawee
2020-8-4 05:48:22

The second one must do, right? It definitely needs to evaluate rest-expr to see what’s going on inside it.


notjack
2020-8-4 05:48:29

oh I think I get why - because stream-rest has to throw if the stream is empty


sorawee
2020-8-4 05:48:30

The first one is weird, I agree


notjack
2020-8-4 05:49:26

okay yes (stream-rest (stream)) throws immediately, so I see where the forcing happens


notjack
2020-8-4 05:49:56

it has to determine that the stream is not empty. but in the case of a stream created with stream-cons, you can check that it’s not empty without forcing either the first or rest expressions


sorawee
2020-8-4 05:50:12

Correct


notjack
2020-8-4 05:50:44

so if the goal is to be maximally lazy, then the behavior of stream-rest is good, because it doesn’t need to force the rest - it just needs to prove that a rest exists


sorawee
2020-8-4 05:50:51

Not really


sorawee
2020-8-4 05:50:54

Consider:


sorawee
2020-8-4 05:51:11

(stream? (stream-rest (stream-cons 1 (error 'bad)))) (stream-empty? (stream-rest (stream-cons 1 (error 'bad))))


notjack
2020-8-4 05:51:31

first one passes and second one throws?


sorawee
2020-8-4 05:51:33

The first one returns #t. The second one errors


sorawee
2020-8-4 05:51:34

Yep


notjack
2020-8-4 05:51:39

yeah that seems correct to me


notjack
2020-8-4 05:51:46

it does create a weird thing though


notjack
2020-8-4 05:51:50

empty streams can throw!


sorawee
2020-8-4 05:52:15

I mean, it fits my mental model of what the stream library is doing, but I am not sure if I would describe it as “correct”


notjack
2020-8-4 05:52:31

yeah, same


notjack
2020-8-4 05:52:52

I’ll take a transducer pipeline or for loop any day over this, I can actually tell when evaluation starts and ends with those


notjack
2020-8-4 05:56:13

oh, an added wrinkle. this code is not as lazy as I would expect: #lang debug racket (stream* #R(stream 1 2 3)) it prints out this: (stream 1 2 3) = #<stream> #<stream> even though I would have assumed that (stream* s) was equivalent to (stream-rest (stream-cons 'ignored s))


sorawee
2020-8-4 05:57:20

Yep, I found that, too


sorawee
2020-8-4 05:57:33

There are a lot of weird things in racket/stream


sorawee
2020-8-4 05:58:24

Here’s another one: stream-take will screw up a multivalued stream, but stream-tail and stream-append will not.


notjack
2020-8-4 05:59:26

ugh


notjack
2020-8-4 06:00:54

I think “avoid streams” might be reasonable general advice at this point


sorawee
2020-8-4 06:01:36

Writing examples for these functions is very difficult. There are so many things to concern about


sorawee
2020-8-4 06:01:55

Multiple values, when things are forced, memoization.


notjack
2020-8-4 06:02:24

yeah and it’s really hard to tell what behavior is intentional


laurent.orseau
2020-8-4 06:49:13

@sorawee maybe bundle a few issues?


sorawee
2020-8-4 06:50:37

Several multiple values issues can be fixed, I think. PR submitted for stream-take, and I think I can handle stream-map, stream-filter, and stream-add-between, too.