How do I use currying with an arbitrary number of arguments in Racket? - arguments

Let's say I want to define a function like format in curried syntax. How would I handle the additional arguments that could be passed through? For example, I would want to be able to do something like the following:
(((format "~a ~a") 5) 9)

Yep, that's tricky. The question is how format is supposed to know that it's got all of its arguments. I'm guessing that you're hoping that format will parse its output string and determine how many arguments it needs, then fire when it's received the right number of arguments. You're going to wind up rolling it yourself. Something like this (NOTE: UNTESTED CODE):
#lang racket
;; given a format-string, return a curried function that
;; accepts the format-string's arguments one at a time
(define (curried-format fmt-str)
(define num-args-expected (count-format-string-args fmt-str))
(define (so-far->waiter so-far)
(lambda (arg)
(define new-args (cons arg so-far))
;; do we have all of the args?
(if (= (length new-args) num-args-expected)
;; yay! call format:
(apply format fmt-str (reverse new-args))
;; nope: wait for some more:
(so-far->waiter new-args))))
(so-far->waiter '()))

Related

racket: Why doesn't apply work with "bitmap" function?

I have been cautioned against using eval in my code. However, in this bit of racket code, I can get eval working but not something more recommended, like apply. Here is the code:
(require 2htdp/image)
(define (get_img filename)
(let ([img (eval `(bitmap ,filename))])
(image->color-list mask)
))
I tried doing the naive replace of eval with apply:
(require 2htdp/image)
(define (get_img filename)
(let ([img (apply `(bitmap ,filename))])
(image->color-list mask)
))
and when I run it I get:
; apply: arity mismatch;
; the expected number of arguments does not match the given number
; expected: at least 2
; given: 1
; [,bt for context]
I have tried a few permutations of this code, but to no avail. I was hopeful this one
(let ([img (apply bitmap `(filename))])
(image->color-list img)`)
would work, but clearly there's still something I'm not understanding
EDIT:
The first thing I tried, with error message:
> (require 2htdp/image)
> (define (get_img filename)
(let ([img (bitmap filename)])
(image->color-list img)))
; readline-input:6:15: bitmap: expected a path with a / in it
; in: (bitmap filename)
; [,bt for context]
Another failed attempt:
> (define (get_img filename)
(let ([img (apply bitmap (list filename))])
(image->color-list mask)))
; readline-input:16:20: bitmap: bad syntax
; in: bitmap
; [,bt for context]
You're using it wrong. So behind the variable + there is a procedure object which can be applied. These are equal:
(+ (* 2 3) 5) ; ==> 11
(apply + (list (* 2 3) 5)) ; ==> 11
(apply + `(,(* 2 3) 5)) ; ==> 11
In your example you are using bitmap and it isn't a procedure at all but a macro and it seems it is to get bitmaps from a racket package and with strings it expects at least a slash since images should't be in the package root. You should replace it with bitmap/file which is a procedure and takes a file path absolute or relative to current working directory instead.
In your example (apply `(bitmap/file ,filename) you are passing apply a list as first argument instead of a procedure object and a last argument with parameters.
In your example (apply bitmap/file `(filename)) you are applying bitmap with a literal list with a symbol filename which has nothing to do with the variable with the same name. You were so close since I think you wanted (apply bitmap/file `(,filename)) which is a funny way of doing (apply bitmap/file (list filename)). What I don't understand is why can't you just do this:
(define (get_img filename)
(let ([img (bitmap/file filename)])
(image->color-list mask)))
The combination apply and bitmap doesn't work together because bitmap is not a function. Note that the entry in the documentation on bitmap says "Syntax" and not "procedure".
If f is a function, then (apply f (list a b c)) will compute (f a b c).
However, bitmap is not a function, it is a "special form".
You are in luck though, because bitmap/file is a function, so you can use that instead.
I think it's worth understanding what apply is useful for, in Racket or other Lisp-1s. In almost all code it is useful when you have a function and a bunch of arguments but you don't know how many there are, and you want to call the function with those arguments. What that means in practise is:
'you have a function' so in particular you are not trying to evaluate some macro form or something else special;
'you have a bunch of arguments' means you have a list of arguments.
If you know how many arguments you have then there is almost never a reason to use apply unless the arguments are already in a list and you don't want to bother extracting them: (apply cons l) might be easier than (cons (first l) (second l)) say.
In particular if you are trying to call a function with a single argument which you know then apply is definitely not what you want. And similarly it is never what you want if the 'function' is a macro.
In Lisp-2s there is an additional use for apply: calling a function which is the value of a variable. This is needed because, as a silly example, (let ((x (lambda (...) ...))) ... (x ...) ...) won't work in a Lisp-2. Lisp-2s have an additional function, funcall, which does what apply does when you know the number of arguments: you don't need funcall in a Lisp-1.
The cases where eval is useful are even rarer. There are some, but almost all the time it is at best a confusion and at worst a terrifying security problem: what does
(define (terror x) (eval `(list x)))
Do when it is called? Answer: anything at all that the language is capable of doing: (terror '(launch-the-nukes)), say.

Typed Racket - dynamic function calls (string to procedure) revisited

About a year ago, #soegaard provided an answer to an interesting problem - how to take a string and return the procedure named in that string. The solution was simple and elegant.
Enter typed racket and a twist.
I can make it work in typed racket as long as it returns only functions with the same arity, for example (-> Number Number Number), but if I try to have it able to return functions with different arities, such as shown below, I cannot figure out how to make the require/typed call work.
Here is the modified file with my second function with a different arity.
#lang racket
(provide string->procedure add square)
(define (add x y)
(+ x y))
(define (square x)
(sqr x))
(define ns (variable-reference->namespace (#%variable-reference)))
(define (string->procedure s)
(define sym (string->symbol s))
(eval sym ns))
(string->procedure "add")
((string->procedure "add") 1 2)
((string->procedure "square") 5)
And here is the call that only works with the "add" fuction or any other function that takes two numbers and returns one number.
#lang typed/racket
(require/typed "string-procedure.rkt"
[string->procedure
(-> String (-> Number Number Number))]
[add (-> Number Number Number)]
[square (-> Number Number)])
I've tried using case-> and unions to no avail. Using case-> for the return type at least will run but then it fails all calls.
In case you think I'm nuts for trying this, what I'm trying to do is take the result of a database call, a string, and determine the correct procedure to call to access the appropriate data element in a struct. I can do it with a long case statement, but I was hoping for a more elegant solution.
Thank you.
I don't think you want to use eval, or to solve this problem in quite this way. Specifically: what if the database contains a string that refers to a function that doesn't exist, or a function that you didn't want to have called? This is how security problems arise.
I would say that in this case, you'd probably be willing to specify the names of the procedures that are "legal", and you can probably do that easily with a macro that doesn't mangle hygiene too badly:
#lang typed/racket
;; defines the 'db-callable' syntax. Put this in a library file...
(define-syntax (db-callable stx)
(syntax-case stx ()
[(_ fun-name [id ...])
(with-syntax ([(id-strs ...)
(for/list ([id-stx (in-list (syntax->list #'(id ...)))])
(symbol->string (syntax-e id-stx)))])
#'(define (fun-name str)
(match str
[id-str id] ...)))]))
;; here are some functions we want the DB to be able to call
(define (f x) 3)
(define (g x) 4)
;; here's the list of functions we want the db to be able to call:
(db-callable getfun [f g])
((getfun "f") 9)
((getfun "g") 123)

Procedure printf in Scheme

For example, when I use the procedure printf on the list '((2 t r d)), the last line in my output is
'(#<void>)
and the number of times '(#<void>) appears depend on the number of list nested. Can you please explain this for me???
This is my printf function
(define counting
(lambda (lst)
(if (null? lst)
'()
(printf "~a, ~s\n" (car lst) (length (cdr lst))))))
I have try to other procedure like fprintf and using this form
(fprintf (current-output-port) "~a, ~s\n" (car lst) (length (cdr lst)))
Same thing happens!
AFAIK there is no such procedure in the Scheme standard so you might need to add a tag for a implementation that has it. I know racket has printf.
A (display x) (and (printf x) in racket) alone usually don't display so what produced (#<void>) is not in the question. In Scheme every procedure evaluates to a value. To illustrate this try doing:
(map display '(1 2 3 4))
Which will return a list with 4 unspecified values since map makes a list of the results. display (and printf in racket) prints the result of the evaluation of the argument but doesn't need to return anything since the standard doesn't say that is should. Most implementations do this by returning an undefined object, but some actually return the argument too. The main function of them is to do side effect of displaying something on screen and that it has done. for ignoring return values you can use for-each which is map just for side effects.
(for-each display '(1 2 3 4))
When that said in Scheme it's normal that every procedure return something and you misread output with the REPLs printing of the returned values.
You said that 'the last line of your output is '(#<void>) - this is occurring because your Scheme environment is displaying 1) what you want to be printed and 2) the returned value of the evaluated expression. For example
> (list (display 1))
1(#<void>)
The '1' is printed and then the list result is printed. Since you are typing in an interactive session you will always get the returned value displayed. You can't really hide the returned value however most Schemes will recognize a 'undefined' return value and not print it.
> (display 1)
1
In the above, even though display returns #<void> the interpreter knows not to show it.

How to make a Racket macro take an integer argument?

I've been trying to make a macro for 'wrapping' functions, i.e. if I have a function that will take a list and cons the symbol 'a to the first element, it'd normally be defined as lambda (l) (cons 'a l), but I want a macro that will take a function and a list of integer-expression pairs and 'wrap' that function by making a new function that takes the some arguments of the old function and gets the rest based on the pairs, using the integer as a position and the expression as the value to give the function. Something like this:
(wrap list (0 'blah) (2 'bloo) (4 'blee))
Should expand to:
(lambda (a1 a2 . rest)
(apply list 'blah a1 'bloo a2 'blee rest))
Problem is, I don't know how to get the macro to find the value of the integer in the pair, it only knows it as a syntax object. I'm fairly new to macros and this should be fairly simple, I'm just having trouble with the docs, and I can't find any info on the web other than simple tutorials on macros. Any help would be appreciated.
This seems like a crazy macro to want to write, especially when SRFI 26 is so much more intuitive to use. Instead of (wrap list (0 'blah) (2 'bloo) (4 'blee)), you can just use:
(cut list 'blah <> 'bloo <> 'blee <...>)
That is surely much easier to read.
If you really must write such a macro, here's one way to go about it, by transforming usages of wrap into equivalent usages of cut:
(require (for-syntax syntax/parse) srfi/26)
(define-syntax (wrap stx)
(syntax-parse stx
((_ func:expr (idx:nat expr:expr) ...)
(let* ((alist (map cons
(syntax->datum #'(idx ...))
(syntax-e #'(expr ...))))
(upper (add1 (apply max -1 (map car alist))))
(listfunc (lambda (i)
(cond ((assv i alist) => cdr)
(else #'<>)))))
(with-syntax (((args ...) (build-list upper listfunc)))
#'(cut func args ... <...>))))))
The key to answering your question, about how to get the integers given the syntax objects, is by using syntax->datum (for deep syntax-stripping) or syntax-e (for shallow syntax-stripping).
(P.S. (To Racket experts reading this.) I'm very new to syntax-parse, so I'm sure syntax-parse experts can find better ways to write the above. I originally wrote the macro as a syntax-case macro, then slapped on syntax-parse syntax classes. That's it.)

Adding arguments to list causes error

I tried an example where we need to pass a list as arguments and if condition succeeds I want to add result to a new list.
Here's the code:
(define get-description
(lambda (codeValue newList)
(cond
((= (car codeValue) 1) (cons "A" newlist))
((= (car codeValue) 2)(cons "B" newlist))
((= (car codeValue) 3) "C")
(else "Negative numbers are not valid"))))
I pass this as the function call:
(get-description (list 1 2 3) (list))
I get output:
(cons "A" empty)
Output should just show: (A)
I am using DrRacket for writing my programs and have chosen language mode as: Beginning Student.
Why do I get cons and A with "" and empty in my newlist?
Please don't use "Beginning Student" as a language type in Racket. That's a subset specially made for the HtDP book. The languages "racket", "r5rs", "pretty big", are more like real Schemes and should all work for The Little Schemer.
In your arguments list, you have (codeValue newList), but in the program body you refer to newlist. All of the Schemes that I've used are case-sensitive. Changing your newList to newlist made your program run perfectly fine on Chez Scheme and Guile too.
Edit: To clarify, "A" is a string. Scheme also has the additional data type of symbol, which is just a name and nothing else (and is probably what you want here). You probably want to (cons 'A newlist) rather than (cons "A" newlist) if you're expecting (A).
Other Schemes would print just ("A"). Such output is clearly an idiosyncrasy of the Racket language.
As for why the A is in quotation marks, that's because it's a string object, and that's simply how string objects are printed. But if you were to DISPLAY such an object, you'd get the A by its lonesome.

Resources