
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).

@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.

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

You made me click…

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.

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?

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?

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

Yes, but how do I count them?

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

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

@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)

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

I’ll try it out, thanks!

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

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

I generally use that as a last resort

FWIW ec
stands for escape continuation.

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


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.

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

(also being more flexible about indentation)

It might be nice to have some return
syntax though

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

Using actual escape continuations might be too expensive

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

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

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.)

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

That worked! Thanks!

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

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.

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!

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

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

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

@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.

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

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*
.

Agreed on both of those points

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?

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).