tim986
2019-11-28 09:22:59

Not a Racket question per se, but I expect there’s enough functional programmers here who might know: Is there a name for this function: (λ (g) (λ (w . x) (apply g x))) ? i.e. creates a function that adds an ignored argument to the front of the arguments list?

I’ve seen something similar in a Java (:cry:) project; and thought — this must have a name. But all of the textbooks I have don’t seem to think it’s a sensible thing to do (in that they don’t mention it).


laurent.orseau
2019-11-28 14:03:54

@sorawee Yes, read does everything for you (including removing whitespaces) but if you want something a little different it’s more complicated (you can still customize the read-table and the various parameters of read though). AFAIU, Elaborate doesn’t need anything else than read for parsing strings however.


laurent.orseau
2019-11-28 14:05:30

I think you’re looking for function-that-adds-an-ignored-argument-to-the-front-of-the-argument-list : https://docs.racket-lang.org/search/index.html?q=function-that-adds-an-ignored-argument-to-the-front-of-the-argument-list


soegaard2
2019-11-28 14:44:17

You made me click…


plragde
2019-11-28 15:57:39

In Haskell this is ‘const’ (though in Haskell every function has exactly one argument, so the code is simpler) and in combinatory logic, it is the K combinator.


elyandarin
2019-11-28 17:50:30

Thanks for the help! Currently testing sorawee’s solution, which is working so far.

@laurent.orseau I’m a newbie at lisp and also at lexers, so I’m actually not completely sure where in the lexer I would “plug in” with-input-from-string. My very basic understanding of the lexer is that I provide it with a list of patterns to search-and-replace, and then it does the search-and-replace for me. But with with-input-from-string, I need to supply the string. So, to use it, I’m guessing I would… use greedy matching to get the substring between the first and last citation mark? then use "(with-input-from-string lexeme read)" and… do something with the output? Is there an example somewhere I can look at?


elyandarin
2019-11-28 17:56:51

Quick question: how would I count the number of pattern matches in a string? I want to use it to keep track of code indentation, so I’d want to count " " or "\t" in the beginning of a line. I guess I could split off the indentation part and bite off one match at a time, but that seems a bit clumsy? Is there an elegant way to do it?


soegaard2
2019-11-28 18:02:28

The pattern [ \t]* will match any number of spaces and tabs.


elyandarin
2019-11-28 18:03:13

Yes, but how do I count them?


soegaard2
2019-11-28 18:03:44

Since you get one string as the result, you can use string-length on the result.


elyandarin
2019-11-28 18:04:29

yes, but would it report three tabs as the same number as six spaces?


laurent.orseau
2019-11-28 18:05:27

@elyandarin Yes, that’s the idea, though I don’t know the specifics of the library you’re using (and I’m too ~lazy~busy to go check)


soegaard2
2019-11-28 18:06:11

(for/sum ([c result]) (match c [#\space 1] [#\tab 2] [_ 0]))


elyandarin
2019-11-28 18:08:20

I’ll try it out, thanks!


elyandarin
2019-11-28 20:20:22

In other languages, I like to use return statements to avoid excessive nesting. Is there something like that in Racket?


samdphillips
2019-11-28 20:21:29

You could use (let/ec return <your-code-using-return>)


samdphillips
2019-11-28 20:21:47

I generally use that as a last resort


soegaard2
2019-11-28 20:22:35

FWIW ec stands for escape continuation.


samdphillips
2019-11-28 20:22:41

Like if I’m converting some pseudo-code into racket for a first draft.



elyandarin
2019-11-28 20:30:31

Thanks for the quick answer! I guess I can’t just use it to make my small code simpler to overview; if I’m interpreting it right, this would introduce a layer of nesting anyway, so unless I want to use several return statements, it’s kinda not worth it.


samdphillips
2019-11-28 21:03:08

Possibly not. The two most powerful tools against rightward drift are internal define and abstraction.


samdphillips
2019-11-28 21:03:36

(also being more flexible about indentation)


notjack
2019-11-28 21:58:48

It might be nice to have some return syntax though


sorawee
2019-11-28 22:32:39

You could create a macro named def that is just like define, but also wraps let/ec


notjack
2019-11-28 22:35:42

Using actual escape continuations might be too expensive


notjack
2019-11-28 22:36:10

Something more like a static transformation of the function’s body seems better to me


lexi.lambda
2019-11-28 22:55:51

I think escape continuations are pretty cheap in Racket, IIRC.


elyandarin
2019-11-28 23:06:46

BTW, stupid newbie question: How do I, er, escape a variable? If that’s the word? Unquote? If I put an apostrophe before a parenthesis, I can quote it and print the code instead of the code’s result. But can I easily say “no I want the actual value of this one variable inside the quote”? (Trying to find out why an expression works by itself but not inside the program.)


willbanders
2019-11-28 23:14:35

Quasiquote, I believe. It’s something like: `(literal literal ,varaible) Think that works, I know it’s used syntax-wise as #and#,`.


elyandarin
2019-11-28 23:21:09

That worked! Thanks!


notjack
2019-11-29 00:02:29

Yeah, but the code you’d write without escape continuations is free


notjack
2019-11-29 00:07:25

As in, most people who want to write this:

(define (foo x y z) (when (something) (return ...)) ...) …wouldn’t write it like this:

(define (foo x y z) (let/ec return (when (something) (return ...)) ...))) …they’d write this:

(define (foo x y z) (cond [(something) ...] [else ...])) And as far as I know, escape continuations do not get optimized away such that the second code block is transformed into the third (it would be really cool if they were though, so I’d be happy to be corrected there). So I figure there’s got to be some runtime work that an escape-continuation-based implementation of return syntax would introduce.


elyandarin
2019-11-29 01:13:35

OK, here’s a more complicated, open-ended question; I’ll check for feedback tomorrow, if anybody has had the time.

I’ve now finished my first from-scratch Racket function. (Instead of testing a feature or modifying code that does almost what I want.) It’s… kind of ugly, IMO, but as far as I can tell, it does its job; keeping track of indents in code with Python-like whitespace so the tokenizer can process it.

How would it look if it were good Racket code, though? Easy to read, no unnecessary work? How should I have coded it?

#lang br (define (tokenize-indents text) ;function to repeat a string X times (define (string-repeat n str) (string-append* (make-list n str))) (define (indent-process old indent-count code result) (cond ;If at end, return result and add owed dedents [(empty? code) (string-append result (string-repeat old "DEDENT ")) ] [else ; Store the indentation part (define indent (regexp-match #rx"^[\t ]*" (first code))) ; Store the rest of the line (define line (string-trim (first code))) ; Count the number of indents. Thanks, soegaard2! (define indent-count (for/sum ([c (first indent)]) (/ (match c [#\space 1] [#\tab 2] [_ 0]) 2)) ) ;(println indent-count) (cond ;If more than one indent has been added, report error and discount line. Continue. [(indent-count . > . (+ 1 old)) (indent-process old old (rest code) (string-append result "ERROR" (first code) "\n" ))] ;If one indent added, add "INDENT " before line, store in result, continue. [(indent-count . = . (+ 1 old)) (indent-process indent-count indent-count (rest code) (string-append result "INDENT " line "\n" ))] ;If one indent removed, add "DEDENT " before line, store in result, continue. [(indent-count . < . old) (indent-process indent-count indent-count (rest code) (string-append result (string-repeat (- old indent-count) "DEDENT ") line "\n" ))] ;If no indent added or removed, add line, store in result, continue. [else (indent-process old indent-count (rest code) (string-append result line "\n" ) )] ) ] )) (indent-process 0 0 (string-split text "\n") "") ) Thanks!


elyandarin
2019-11-29 01:14:45

Oh yeah, is there a function for syntax highlighting I should use in the code box?


notjack
2019-11-29 01:17:29

@elyandarin Syntax highlighting: I don’t think there is one, unfortunately :(


elyandarin
2019-11-29 01:21:18

OK, time to belatedly go to bed, I’ll check for feedback tomorrow. Good night!


notjack
2019-11-29 01:27:35

@elyandarin As for the code, here’s some things that jump out at me: • Parens on their own line usually isn’t done in lisp. Instead just stack ’em on the end of the last line, and use blank lines to visually separate blocks as desired. • The string-repeat function doesn’t need access to the text variable at all, so consider pulling it out of the tokenize-indents function and into its own standalone thing at the module level. • Instead of defining an indent-process function for and then immediately calling it, just so you can make the recursion work, consider using a named let instead. Example: (let indent-process ([old 0] [indent-count 0] [code text] [result ""]) (cond …)). • Maybe make some small one or two-line helper functions that do the string appending bits, so you can 1) name them and 2) keep the string munging code out of the way of the main logic. • (my personal style) Using the two dots for infix is confusing, gives terrible error messages when mistyped, and has some very surprising behaviors. I don’t think it’s worth doing.


sorawee
2019-11-29 01:57:01

Actually, because all recursive calls traverses through the list by one element, I would even suggest you to use for/fold rather than named let


sorawee
2019-11-29 01:59:55

Another thing I don’t like is how string-append is used in every iteration to accumulate the result. This is likely going to result in an asymptotically worse performance. The usual idiom to do something like this is to accumulate the result as a list, and then convert the list to a string in one go with string-join or string-append* .


notjack
2019-11-29 02:05:45

Agreed on both of those points


sorawee
2019-11-29 02:12:15

Another thing that is weird about this tokenizer is that, usually a tokenizer will produce a list of tokens. But this tokenizer produces a string. My guess is that you are going to feed this into lexer-src-pos later. That’s why you need the output of this first phase to be a string, but that’s likely going to brittle. What will happen, for instance, if user’s code has the string or variable named INDENT? Would your real tokenizer misrecognizes it as an indentation?


sorawee
2019-11-29 02:17:12

Take a look at http://matt.might.net/articles/lexers-in-racket/ section “Analyzing indentation” for a proper way to handle indentation with lexer-src-pos. (The article uses lexer rather than lexer-src-pos however. It’s still matching against regex, but you need to adjust the action expr).