popa.bogdanp
2019-11-4 08:34:17

By default read-line has breaks enabled for me on Racket 7.4 on macOS:

$ racket -e '(read-line)'
^Cuser break
  context...:
   eval-one-top12

Maybe you’ve got a parameterize-break #f somewhere in your code?

$ racket -e '(parameterize-break #f (read-line))'
^C^C^C^C^C^C^Casd
"asd"


sorawee
2019-11-4 09:28:22

Ha! I’m doing read-line in a with-handler’s error handler. That’s probably why…


sorawee
2019-11-4 09:31:25
racket -e '(with-handlers ([exn:fail? (thunk* (read-line))]) (error (quote test)))'

monsieur.toulouse
2019-11-4 14:00:53

@monsieur.toulouse has joined the channel



solrach
2019-11-4 17:07:13

@solrach has joined the channel


badkins
2019-11-4 18:24:19

I touched on this in the beginners channel, but I think it’s a more general question: What are the advantages of having map throw an exception when passed lists of unequal lengths vs. stopping when the shortest list arg is exhausted? I typically want the latter behavior, but maybe I’m missing some advantage.


soegaard2
2019-11-4 18:25:17

It’s to prevent silent errors (which are the most difficult to find).


badkins
2019-11-4 18:27:41

It seems like that error checking should be layered on top though. The current way precludes some niceties. I personally don’t recall ever wanting the current behavior, and if I wanted it, I’d expect to wrap map with a function that checks the lengths.


jaz
2019-11-4 18:28:34

for/list with multiple sequences does what you want


badkins
2019-11-4 18:30:16

@jaz my original motivation was to have the following work on unequal length lists: (define (zipn . lists) (apply map list lists)) I’m guessing that it’s not possible to use for/list in this way, right?


soegaard2
2019-11-4 18:32:39

FWIW map has behaved that way for ever. (At least since 1985 according to R2RS p. 58) https://dspace.mit.edu/bitstream/handle/1721.1/5600/AIM-848.pdf


badkins
2019-11-4 18:32:52

I’d be fine w/ writing my own version of map that works the way I want, but I think the built-in is optimized in a way that would be difficult for me to do.


badkins
2019-11-4 18:34:12

@soegaard2 I agree that a long history is a reasonable factor in designing Racket’s map. Clojure’s impl works the other way FWIW.


badkins
2019-11-4 18:35:05

When I’m faced with this type of decision where there are two reasonable behaviors desired, I consider the relative difficulty of implementing A with B vs. B with A.


badkins
2019-11-4 18:36:24

I suppose I’d have to determine the smallest list, then perform take on all the lists before passing them to the original map


soegaard2
2019-11-4 18:36:37

Expectations differ. In (map list '(a b) '(1 2 3)) there is no third element in ’(a b). Some language would use null or false to indicate a missing value.


badkins
2019-11-4 18:37:38

I know it’s a subjective matter. My hunch is that more often folks would simply want to end when the smallest list is exhausted, but I have no data to back that up :slightly_smiling_face:


soegaard2
2019-11-4 18:39:08

This (for/list ([x (in-list xs)] [y (in-list ys)]) (f x y)) stops when one of the lists xs and ys run out.


soegaard2
2019-11-4 18:40:11

So the question is how fast it is, compared to (map f xs ys).


badkins
2019-11-4 18:41:40

@soegaard2 yes, but see original motivation above - having for/list be a macro is sometimes problematic


soegaard2
2019-11-4 18:42:01

We can put the for/list away in a function.


badkins
2019-11-4 18:42:48

Sure. Maybe we can “fix” it in Rhombus :wink:


soegaard2
2019-11-4 18:47:25

@badkins Try running this. #lang racket (define (map2 f xs ys) (for/list ([x (in-list xs)] [y (in-list ys)]) (f x y))) (define xs (make-list 1000000 42)) (define ys (make-list 1000000 42)) (define (our-map) (map2 + xs ys) (void)) (define (org-map) (map + xs ys) (void)) (for ([_ 3]) (collect-garbage)) (time (our-map)) (for ([_ 3]) (collect-garbage)) (time (org-map))


soegaard2
2019-11-4 18:47:30

I get very close results.


badkins
2019-11-4 18:49:17

@soegaard2 doesn’t that lose the vararg aspect?


sorawee
2019-11-4 18:49:27

@soegaard2 why collect-garbage three times? I see this in the code that I was working with as well


notjack
2019-11-4 18:49:46

Throw on uneven by default, add a keyword argument to enable silently dropping extras


soegaard2
2019-11-4 18:49:47

Well, no point in implementing something complicated, if the simple case is slower.


notjack
2019-11-4 18:50:17

@sorawee I’ve always heard it’s related to generations


notjack
2019-11-4 18:51:43

as in, a single major collection won’t necessarily collect all the garbage it possibly can, because some garbage might be pointed to by other garbage and a single collection will actually just promote it to a longer lived generation


sorawee
2019-11-4 18:52:12

If that is indeed the case, there really should be (collect-garbage 'generations) or something equivalent.


sorawee
2019-11-4 18:52:29

Writing (collect-garbage) three times is just absurd.


jaz
2019-11-4 18:52:43

(collect-garbage 'major)?


sorawee
2019-11-4 18:53:04

I thought 'major is the default mode…. Let me see the docs again


jaz
2019-11-4 18:53:15

you’re right


jaz
2019-11-4 18:53:42

I wonder if it did a major collection prior to 6.3


notjack
2019-11-4 18:53:57

When I need to do that for benchmarking purposes I just write a (ensure-garbage-collector-in-clean-state!) function that does the three collections, to remind myself why the hell I need to write such bizarre code


jaz
2019-11-4 18:54:19

Could also be related to finalization, ephemerons, or any of the things that complicate gc


jaz
2019-11-4 18:55:01

(but I really don’t know)


samth
2019-11-4 18:57:31

yes, it’s because of those things


samth
2019-11-4 19:00:40

it’s of course possible to require arbitrarily many collections to reach a fixed point, but 3 seems like enough


samth
2019-11-4 19:01:02

of course, it doesn’t genuinely reach a fixed point either


soegaard2
2019-11-4 19:02:06

Okay, here is a version that handles multiple lists as well. #lang racket (define (map2 f xs ys) (for/list ([x (in-list xs)] [y (in-list ys)]) (f x y))) (define (map3 f xs ys zs) (for/list ([x (in-list xs)] [y (in-list ys)] [z (in-list zs)]) (f x y z))) (define (map4 f xs ys zs ws) (for/list ([x (in-list xs)] [y (in-list ys)] [z (in-list zs)] [w (in-list ws)]) (f x y z w))) (define (map** f xss) (define done? (ormap empty? xss)) (if done? '() (cons (apply f (map car xss)) (map** f (map cdr xss))))) (define (map* f . xss) (match xss [(list xs) (map f xs)] [(list xs ys) (map2 f xs ys)] [(list xs ys zs) (map3 f xs ys zs)] [(list xs ys zs ws) (map4 f xs ys zs ws)] [xss (map** f xss)]))


notjack
2019-11-4 19:13:27

I really don’t think map should accept multiple lists in the first place, and that a separate zip function should do that.


notjack
2019-11-4 19:13:35

But that’s another discussion entirely


jaz
2019-11-4 19:16:06

FWIW, srfi/1’s map has the behavior that @badkins wants


badkins
2019-11-4 19:17:12

Maybe something to address in Rhombus?


notjack
2019-11-4 19:36:22

Yup


cboekell
2019-11-4 20:43:08

@cboekell has joined the channel


popa.bogdanp
2019-11-4 21:56:52

popa.bogdanp
2019-11-4 22:04:45

(define-syntax (map* stx)
  (define-syntax-class lst
    (pattern e:expr
             #:with name (datum->syntax #'e (gensym 'map*))
             #:with binder #'[name (in-list e)]))

  (syntax-parse stx
    [(_ f xs:lst ...+)
     #'(for/list (xs.binder ...)
         (f xs.name ...))]))

it is with macros :smile:


badkins
2019-11-4 22:55:59

Didn’t work: #lang racket (require (for-syntax syntax/parse) syntax/parse/define) (define-syntax (map* stx) (define-syntax-class lst (pattern e:expr #:with name (datum->syntax #'e (gensym 'map*)) #:with binder #'[name (in-list e)])) (syntax-parse stx [(_ f xs:lst ...+) #'(for/list (xs.binder ...) (f xs.name ...))])) (define numbers '(1 2 3 4)) (define strings '("foo" "bar" "baz")) (define symbols '(a b c)) (define lists '((4 5 6) () (7 8))) ;(define (zipn . lists) ; (apply map list lists)) ;(zipn strings symbols lists) (define (zipn . lists) (apply map* list lists)) (zipn numbers strings symbols lists) The commented out code does work, but only with equal length list args.


badkins
2019-11-4 22:56:14

I get a bad syntax error with your code.


popa.bogdanp
2019-11-5 06:33:26

Sorry, I should’ve been more clear. map* is really your zipn.


popa.bogdanp
2019-11-5 06:34:13

(map* list numbers strings symbols lists) should work fine, but you can’t apply a macro.