Is there a way to return no value?

3,175 views
Skip to first unread message

David K. Storrs

unread,
Dec 11, 2015, 1:16:40 AM12/11/15
to Racket Users
Question: Is there a way to make a function return nothing -- not '() or #void, but actually nothing?


I'm playing around with HTML parsing as a learning exercise. The current exercise is writing a look-down function which can be handed an HTML value produced by the html library and a function, run the function across the collect the results, and return them. I would use this for things like "find every link in the following document" or "capitalize every use of the word 'kumquat'" or etc.

I would like to have a way to say "this element isn't interesting, just ignore it" so that I don't get spurious return values in the results list. I can't see a way to do that...I can exclude the values afterwards, but that's not really what I was looking for, and opens the door to false negatives.

Here's some simplified test code (removing all the HTML stuff for clarity) to show the point:


----------------
(define (foo-1 x) (if (eq? x "foo") #t '()))
(define (foo-2 x) (if (eq? x "foo") #t (values)))

(map foo-1 '(a b "foo" "bar"))
(filter (lambda (x) (not (null? x))) (map foo-1 '(a b "foo" "bar")))
(map foo-2 '(a b "foo" "bar"))

[dstorrs@localhost:~/personal/study/scheme/spider:<master*>]$ racket test.rkt
racket test.rkt
'(() () #t ())
'(#t)
result arity mismatch;
expected number of values not received
expected: 1
received: 0
values...:
context...:
/Users/dstorrs/personal/study/scheme/spider/test.rkt: [running body]
----------------

Instead of a 'result arity' crash, I would have liked to get '(#t) back, same a if I'd generated the list and then filtered it.

Is there a way to do this?


For the record, here's the actual look-down function:

(require (prefix-in h: html)
(prefix-in x: xml))

(define (look-down el action)
(match el
[(struct h:html-full (attributes content))
(apply append (map action content))]

[(struct h:html-element (attributes))
(action el)]

[ _ '()]))

Matthew Butterick

unread,
Dec 11, 2015, 2:22:27 AM12/11/15
to Racket Users
Answer: Yes, if you redefine nothingness. You can exploit the fact that appending (or splicing) an empty list will cause it to disappear (meaning, no #<void> residue).

#lang racket
(require rackunit)
(define (foo-3 x) (if (eq? x "foo") (list #t) empty))
(check-equal? (append-map foo-3 '(a b "foo" "bar")) '(#t))

Jay McCarthy

unread,
Dec 11, 2015, 7:15:13 AM12/11/15
to David K. Storrs, Racket Users
Hi David,

You can return nothing with (values). The function "values" returns
any number of values... (values 1 2 3) returns 3 values, (values 1)
returns 1 value, and (values) returns 0 values.

Jay
> --
> You received this message because you are subscribed to the Google Groups "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.



--
Jay McCarthy
Associate Professor
PLT @ CS @ UMass Lowell
http://jeapostrophe.github.io

"Wherefore, be not weary in well-doing,
for ye are laying the foundation of a great work.
And out of small things proceedeth that which is great."
- D&C 64:33

WarGrey Gyoudmon Ju

unread,
Dec 11, 2015, 9:32:26 AM12/11/15
to Jay McCarthy, David K. Storrs, Racket Users
Oh, that's interesting.

> (void? (values))
Type Checker: expected single value, got multiple (or zero) values in: (values)

David K. Storrs

unread,
Dec 11, 2015, 1:23:20 PM12/11/15
to Racket Users, david....@gmail.com
On Friday, December 11, 2015 at 4:15:13 AM UTC-8, Jay McCarthy wrote:
> Hi David,
>
> You can return nothing with (values). The function "values" returns
> any number of values... (values 1 2 3) returns 3 values, (values 1)
> returns 1 value, and (values) returns 0 values.
>
> Jay


Hi Jay,

I actually tried that in my foo-2 function: (define (foo-2 x) (if (eq? x "foo") #t (values)))

That's the one that led to the arrity contract failure.

(And I just realized that I should have named them foo1 and foo2 because it looks like I named it "foo minus 2". Oh well.)


---------------


Matthew Butterick wrote:
> Answer: Yes, if you redefine nothingness. You can exploit the fact that
> appending (or splicing) an empty list will cause it to disappear (meaning, no
> #<void> residue).

> #lang racket
> (require rackunit)
> (define (foo-3 x) (if (eq? x "foo") (list #t) empty))

> (check-equal? (append-map foo-3 '(a b "foo" "bar")) '(#t))


That was exactly what I wanted! Thank you very much. Also, I hadn't yet discovered "append-map", so that's another thank you.

Matthew Butterick

unread,
Dec 11, 2015, 4:53:43 PM12/11/15
to Racket Users, david....@gmail.com
PS. I'm assuming that you're using `eq?` here in deliberate preference to `equal?`. Because `eq?` is not reliable for string comparisons.

David Storrs

unread,
Dec 11, 2015, 5:27:16 PM12/11/15
to Matthew Butterick, Racket Users
On Fri, Dec 11, 2015 at 1:53 PM, Matthew Butterick <chro...@gmail.com> wrote:
PS. I'm assuming that you're using `eq?` here in deliberate preference to `equal?`. Because `eq?` is not reliable for string comparisons.

Ah.  No, I did not realize that.  I thought that Racket worked on a flyweight pattern where all strings of the same characters were eq?  -- isn't that what interned symbols are about?
 

Jon Zeppieri

unread,
Dec 11, 2015, 5:32:17 PM12/11/15
to David Storrs, Matthew Butterick, Racket Users
On Fri, Dec 11, 2015 at 5:27 PM, David Storrs <david....@gmail.com> wrote:
>
>
> On Fri, Dec 11, 2015 at 1:53 PM, Matthew Butterick <chro...@gmail.com>
> wrote:
>>
>> PS. I'm assuming that you're using `eq?` here in deliberate preference to
>> `equal?`. Because `eq?` is not reliable for string comparisons.
>
>
> Ah. No, I did not realize that. I thought that Racket worked on a
> flyweight pattern where all strings of the same characters were eq? --
> isn't that what interned symbols are about?


Yes, but strings aren't interned symbols (or symbols, at all, in fact).
Many (most?) uses of strings don't benefit from interning.

-Jon

Alex Knauth

unread,
Dec 11, 2015, 5:34:36 PM12/11/15
to David Storrs, Matthew Butterick, Racket Users

On Dec 11, 2015, at 5:27 PM, David Storrs <david....@gmail.com> wrote:



On Fri, Dec 11, 2015 at 1:53 PM, Matthew Butterick <chro...@gmail.com> wrote:
PS. I'm assuming that you're using `eq?` here in deliberate preference to `equal?`. Because `eq?` is not reliable for string comparisons.

Ah.  No, I did not realize that.  I thought that Racket worked on a flyweight pattern where all strings of the same characters were eq?  -- isn't that what interned symbols are about?

Some examples:

> (eq? "foo" "foo")
#t
> (eq? "foo" (symbol->string 'foo))
#f
> (eq? (symbol->string 'foo) (symbol->string 'foo))
#f
> (eq? "foo" (datum-intern-literal (symbol->string 'foo)))
#t

So, string constants produced by the reader seem to be eq?, but strings produced by any other operation, such as symbol->string, string-append, format, etc, will not be.


David Storrs

unread,
Dec 11, 2015, 5:35:21 PM12/11/15
to Jon Zeppieri, Matthew Butterick, Racket Users
Aha.  Good to know, thank you guys.  That would have made for some difficult-to-find bugs in the future.

 

-Jon

Reply all
Reply to author
Forward
0 new messages