Re: [racket-users] structs vs classes

2017-12-07 Thread Greg Hendershott
p.s. The package supplies a `define-struct-updaters` macro separately.
This is handy because you can use it on any structs -- including those
from other modules you don't control. If you do this frequently for
your own structs, you could of course write a little macro to `(begin
(struct id ) (define-struct-updaters id))`.

On Thu, Dec 7, 2017 at 2:19 PM, Greg Hendershott
 wrote:
> On Thu, Dec 7, 2017 at 11:08 AM, David Storrs  wrote:
>> assembled.  There's no reason not to use functional update, but I
>> haven't seen a straightforward way to do that since there is no
>> 'set-db-chunk-foo' as opposed to 'set-db-chunk-foo!'.   I guess I need
>> to use 'struct-copy'?  At that point it seems more sensible to have a
>
> There is a struct-update package:
>
>   https://pkgd.racket-lang.org/pkgn/package/struct-update
>
> Doc:
>
>   http://docs.racket-lang.org/struct-update/index.html

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] structs vs classes

2017-12-07 Thread Greg Hendershott
On Thu, Dec 7, 2017 at 11:08 AM, David Storrs  wrote:
> assembled.  There's no reason not to use functional update, but I
> haven't seen a straightforward way to do that since there is no
> 'set-db-chunk-foo' as opposed to 'set-db-chunk-foo!'.   I guess I need
> to use 'struct-copy'?  At that point it seems more sensible to have a

There is a struct-update package:

  https://pkgd.racket-lang.org/pkgn/package/struct-update

Doc:

  http://docs.racket-lang.org/struct-update/index.html

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] structs vs classes

2017-12-07 Thread Shu-Hung You
I'm not sure whether the following is a good idea, but it more or less
works. The impersonate-struct function allows one to redirect the some
or all of accessor and mutator functions on *individual struct
instances*. If the actual constructor is hidden from the user and only
a ``smart'' constructor is exported, then we can put impersonators on
all struct instances.

#lang racket

(provide
 smart-db-chunk-approach-1
 smart-db-chunk-approach-2

 (except-out
  (struct-out db-chunk)
  make-db-chunk))

(struct db-chunk (scratchdir-path chunkdir-path) #:mutable
  #:constructor-name make-db-chunk
  #:transparent ;; for impersonators
  ;; and perhaps guards
  )


;; approach 1: save path-string? in struct fields and redirect accessors
(define (smart-db-chunk-approach-1 . args)
  (impersonate-struct
   (apply make-db-chunk args)

   db-chunk-scratchdir-path
   (λ (chnk v) (if (path? v) (path->string v) v))
   db-chunk-chunkdir-path
   (λ (chnk v) (if (path? v) (path->string v) v

(define s1 (smart-db-chunk-approach-1 (string->path "/usr") "/var"))
(string? (db-chunk-scratchdir-path s1))



;; approach 2: redirect mutators and only save string? in struct fields
(define (smart-db-chunk-approach-2 . args)
  (impersonate-struct
   (apply make-db-chunk args)

   set-db-chunk-scratchdir-path!
   (λ (chnk v) (if (path? v) (path->string v) v))
   set-db-chunk-chunkdir-path!
   (λ (chnk v) (if (path? v) (path->string v) v

(define s2 (smart-db-chunk-approach-2 "/usr" "/var"))
(set-db-chunk-chunkdir-path! s2 (string->path "/bin"))
(string? (db-chunk-scratchdir-path s2))

--Shu-Hung

On Thu, Dec 7, 2017 at 10:10 AM, David Storrs  wrote:
> Wow.  Thank you, Matthias.  That's impressive.
>
> On Thu, Dec 7, 2017 at 10:46 AM, Matthias Felleisen
>  wrote:
>>
>> On Dec 6, 2017, at 10:45 PM, David Storrs  wrote:
>>
>> I have a struct that looks like this (simplified for concision):
>>
>> (struct db-chunk (scratchdir-path chunkdir-path) #:mutable)
>>
>> I'd like to ensure that:
>>
>> 1) At least one of the fields must be set at creation
>> 2) Both fields will accept only a path-string? value
>> 3) Both fields will return string? when accessed, as that makes it
>> easier to insert into the DB when the time comes.
>>
>> I can put a #:guard parameter on db-chunk that will check #1 and #2
>> and (if necessary) transform the input into a string in order to
>> satisfy #3, but that only works at initialization.  From there I would
>> need to redefine the mutator:
>>
>> (struct db-chunk (scratchdir-path chunkdir-path) #:mutable)
>>
>> (let ([old-mut set-db-chunk-scratchdir-path!])
>>  (set! set-db-chunk-scratchdir-path!
>>  (lambda (chnk val)
>>(old-mut chnk (if (path? val) (path->string val) val))
>>
>> Alternatively, I could define my struct and then make a bunch of
>> custom manipulation functions, but then (a) I've got these unguarded
>> functions floating around and (b) I've given up a major advantage of
>> structs, which is their concision.
>>
>>
>> Consider the introduction of a syntax transformer that refines struct
>> according to your needs. The sketch below is a bit brittle but if you
>> need to strengthen it, I am sure our virtual macrologists can help.
>>
>> — Matthias
>>
>>
>>
>>
>> #lang racket
>>
>> ;; ---
>> ;; in some module/file:
>>
>> #;
>> (provide
>>  ;; SYNTAX(struct/ccc Id [Id ...] MINUS Id ...)
>>  ;; SEMANTICS (struct/ccc a [b ...] MINUS c ...) is like
>>  ;;  (struct a [b ...]) but hides the struct-defined functions
>>  ;;  c ...
>>  struct/ccc)
>>
>> (define-syntax (struct/ccc stx)
>>   (syntax-case stx (MINUS PLUS)
>> [(struct/ccc s [a ...] (MINUS x ...) (PLUS [f ex] ...))
>>  ;; ==>
>>  (let ((my-require (datum->syntax stx '(require 'server
>>#`(begin
>>(module server #,(datum->syntax stx 'racket)
>>  (provide
>>   (except-out (struct-out s) x ...))
>>  (struct s [a ...])
>>  (set! f ex) ...)
>>#,my-require))]))
>>
>> ;; ---
>> ;; require the module here
>>
>> (struct/ccc s [a b c]
>> (MINUS s-a)
>> (PLUS [s-b (let ((old s-b)) (λ (x) (define b (old x)) (* 2
>> b)))]))
>> (define s0 (s 1 2 3))
>> (s-b s0)
>
> --
> 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+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
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+unsubscr...@googlegroups.com.
For more options, visit 

Re: [racket-users] structs vs classes

2017-12-07 Thread David Storrs
Wow.  Thank you, Matthias.  That's impressive.

On Thu, Dec 7, 2017 at 10:46 AM, Matthias Felleisen
 wrote:
>
> On Dec 6, 2017, at 10:45 PM, David Storrs  wrote:
>
> I have a struct that looks like this (simplified for concision):
>
> (struct db-chunk (scratchdir-path chunkdir-path) #:mutable)
>
> I'd like to ensure that:
>
> 1) At least one of the fields must be set at creation
> 2) Both fields will accept only a path-string? value
> 3) Both fields will return string? when accessed, as that makes it
> easier to insert into the DB when the time comes.
>
> I can put a #:guard parameter on db-chunk that will check #1 and #2
> and (if necessary) transform the input into a string in order to
> satisfy #3, but that only works at initialization.  From there I would
> need to redefine the mutator:
>
> (struct db-chunk (scratchdir-path chunkdir-path) #:mutable)
>
> (let ([old-mut set-db-chunk-scratchdir-path!])
>  (set! set-db-chunk-scratchdir-path!
>  (lambda (chnk val)
>(old-mut chnk (if (path? val) (path->string val) val))
>
> Alternatively, I could define my struct and then make a bunch of
> custom manipulation functions, but then (a) I've got these unguarded
> functions floating around and (b) I've given up a major advantage of
> structs, which is their concision.
>
>
> Consider the introduction of a syntax transformer that refines struct
> according to your needs. The sketch below is a bit brittle but if you
> need to strengthen it, I am sure our virtual macrologists can help.
>
> — Matthias
>
>
>
>
> #lang racket
>
> ;; ---
> ;; in some module/file:
>
> #;
> (provide
>  ;; SYNTAX(struct/ccc Id [Id ...] MINUS Id ...)
>  ;; SEMANTICS (struct/ccc a [b ...] MINUS c ...) is like
>  ;;  (struct a [b ...]) but hides the struct-defined functions
>  ;;  c ...
>  struct/ccc)
>
> (define-syntax (struct/ccc stx)
>   (syntax-case stx (MINUS PLUS)
> [(struct/ccc s [a ...] (MINUS x ...) (PLUS [f ex] ...))
>  ;; ==>
>  (let ((my-require (datum->syntax stx '(require 'server
>#`(begin
>(module server #,(datum->syntax stx 'racket)
>  (provide
>   (except-out (struct-out s) x ...))
>  (struct s [a ...])
>  (set! f ex) ...)
>#,my-require))]))
>
> ;; ---
> ;; require the module here
>
> (struct/ccc s [a b c]
> (MINUS s-a)
> (PLUS [s-b (let ((old s-b)) (λ (x) (define b (old x)) (* 2
> b)))]))
> (define s0 (s 1 2 3))
> (s-b s0)

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] structs vs classes

2017-12-07 Thread David Storrs
On Wed, Dec 6, 2017 at 11:23 PM, Philip McGrath
 wrote:
>
>> 1) At least one of the fields must be set at creation
>
>
> What does it mean for one of the fields not to be set at creation? Is there
> implicitly some not-initialized value like #f that is also valid for the
> constructor? Do you want your accessor functions to raise an error if
> someone tries to access a non-initialized field?

Our system pushes chunks of data around.  A chunk is a small file on
disk that lives in the '.chunks/' directory.  If it's being actively
worked with then it gets copied to the '.scratch/' directory.  This
struct tracks where the file currently exists; you wouldn't create one
of these if the chunk isn't on disk (there are other mechanisms for
that), so at least one and possibly both of the paths must be set.

Yes, the fields will default to #f, although throwing an exception on
access is also an option.

>
>>
>> 3) Both fields will return string? when accessed, as that makes it
>> easier to insert into the DB when the time comes.
>
>
> As you may know, the reference warns that "some paths may not be
> representable as strings" and that "decoding and re-encoding the path’s byte
> string [to a string] may lose information". You may be able to ignore this
> in your particular circumstances, but you might consider using the
> byte-string representation of paths (which doesn't have this problem), or
> this might be an argument in favor of multiple accessor functions/methods
> which convert to different formats.
>
> More broadly, what is the meaning of mutating one of the fields of your
> struct? Is there a compelling reason not to use an immutable struct and
> functional update?

Good question, I should have provided the context originally.  Sorry for that.

Mutating the fields would usually happen when a chunk gets copied to
the scratchdir to be worked with, deleted from the scratchdir when
you're done with it, or moved to the chunksdir if it's finished being
assembled.  There's no reason not to use functional update, but I
haven't seen a straightforward way to do that since there is no
'set-db-chunk-foo' as opposed to 'set-db-chunk-foo!'.   I guess I need
to use 'struct-copy'?  At that point it seems more sensible to have a
small module pertaining solely to managing chunk information that uses
a hash for a backing store, since hashes are easier to work with.  Or
go full OO and create an object that tracks it for me.


>> (let ([old-mut set-db-chunk-scratchdir-path!])
>>   (set! set-db-chunk-scratchdir-path!
>>   (lambda (chnk val)
>> (old-mut chnk (if (path? val) (path->string val) val))
>
>
> Regardless of anything else, I would definitely not do this. To me, this
> would "work against the idea of functional programming" far more than using
> a class. (Also, I have the impression that mutating a module-level variable
> can have performance implications.)

Oh, absolutely.  This is krunky as heck and when I found myself
considering it is when I realized I needed to back away from the
keyboard and ask the list for the right way to do it.


>
> If I wanted to use a mutable struct with the sorts of wrappers you describe,
> I would write a module with the struct definition and the wrapper functions
> and export only the interface with guards, perhaps using rename-out (or the
> rename option with contract-out) to give the wrappers nice external names. I
> will sometimes even use a submodule for the purpose of packaging up an
> abstraction and giving only a high-level interface to surrounding code.
>
> Alternatively, using a class could be a valid choice, but I would probably
> only consider that if you have other code that uses racket/class: it seems
> like a fairly large set of concepts to add just for this. I have come to
> appreciate racket/class in the right circumstances, especially for
> representing stateful objects or lots of variants with slight variations in
> method behavior. I don't think there's anything inherently un-functional
> about classes (and I'd say an immutable object is more functional than a
> mutable struct).


Good advice, thanks.

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] structs vs classes

2017-12-07 Thread Matthias Felleisen

> On Dec 6, 2017, at 10:45 PM, David Storrs  wrote:
> 
> I have a struct that looks like this (simplified for concision):
> 
> (struct db-chunk (scratchdir-path chunkdir-path) #:mutable)
> 
> I'd like to ensure that:
> 
> 1) At least one of the fields must be set at creation
> 2) Both fields will accept only a path-string? value
> 3) Both fields will return string? when accessed, as that makes it
> easier to insert into the DB when the time comes.
> 
> I can put a #:guard parameter on db-chunk that will check #1 and #2
> and (if necessary) transform the input into a string in order to
> satisfy #3, but that only works at initialization.  From there I would
> need to redefine the mutator:
> 
> (struct db-chunk (scratchdir-path chunkdir-path) #:mutable)
> 
> (let ([old-mut set-db-chunk-scratchdir-path!])
>  (set! set-db-chunk-scratchdir-path!
>  (lambda (chnk val)
>(old-mut chnk (if (path? val) (path->string val) val))
> 
> Alternatively, I could define my struct and then make a bunch of
> custom manipulation functions, but then (a) I've got these unguarded
> functions floating around and (b) I've given up a major advantage of
> structs, which is their concision.
> 

Consider the introduction of a syntax transformer that refines struct 
according to your needs. The sketch below is a bit brittle but if you 
need to strengthen it, I am sure our virtual macrologists can help. 

— Matthias




#lang racket

;; ---
;; in some module/file:

#;
(provide
 ;; SYNTAX(struct/ccc Id [Id ...] MINUS Id ...)
 ;; SEMANTICS (struct/ccc a [b ...] MINUS c ...) is like
 ;;  (struct a [b ...]) but hides the struct-defined functions
 ;;  c ...
 struct/ccc)

(define-syntax (struct/ccc stx)
  (syntax-case stx (MINUS PLUS)
[(struct/ccc s [a ...] (MINUS x ...) (PLUS [f ex] ...))
 ;; ==>
 (let ((my-require (datum->syntax stx '(require 'server
   #`(begin
   (module server #,(datum->syntax stx 'racket)
 (provide 
  (except-out (struct-out s) x ...))
 (struct s [a ...])
 (set! f ex) ...)
   #,my-require))]))

;; ---
;; require the module here

(struct/ccc s [a b c]
(MINUS s-a)
(PLUS [s-b (let ((old s-b)) (λ (x) (define b (old x)) (* 2 b)))]))
(define s0 (s 1 2 3))
(s-b s0)

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] structs vs classes

2017-12-06 Thread Philip McGrath
> 1) At least one of the fields must be set at creation
>

What does it mean for one of the fields not to be set at creation? Is there
implicitly some not-initialized value like #f that is also valid for the
constructor? Do you want your accessor functions to raise an error if
someone tries to access a non-initialized field?


> 3) Both fields will return string? when accessed, as that makes it
> easier to insert into the DB when the time comes.
>

As you may know, the reference
warns that "some
paths may not be representable as strings" and that "decoding and
re-encoding the path’s byte string [to a string] may lose information". You
may be able to ignore this in your particular circumstances, but you might
consider using the byte-string representation of paths (which doesn't have
this problem), or this might be an argument in favor of multiple accessor
functions/methods which convert to different formats.

More broadly, what is the meaning of mutating one of the fields of your
struct? Is there a compelling reason not to use an immutable struct and
functional update?


> (let ([old-mut set-db-chunk-scratchdir-path!])
>   (set! set-db-chunk-scratchdir-path!
>   (lambda (chnk val)
> (old-mut chnk (if (path? val) (path->string val) val))
>

Regardless of anything else, I would definitely not do this. To me, this
would "work against the idea of functional programming" far more than using
a class. (Also, I have the impression that mutating a module-level variable
can have performance implications.)

If I wanted to use a mutable struct with the sorts of wrappers you
describe, I would write a module with the struct definition and the wrapper
functions and export only the interface with guards, perhaps using
rename-out (or the rename option with contract-out) to give the wrappers
nice external names. I will sometimes even use a submodule for the purpose
of packaging up an abstraction and giving only a high-level interface to
surrounding code.

Alternatively, using a class could be a valid choice, but I would probably
only consider that if you have other code that uses racket/class: it seems
like a fairly large set of concepts to add just for this. I have come to
appreciate racket/class in the right circumstances, especially for
representing stateful objects or lots of variants with slight variations in
method behavior. I don't think there's anything inherently un-functional
about classes (and I'd say an immutable object is more functional than a
mutable struct).

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[racket-users] structs vs classes

2017-12-06 Thread David Storrs
I have a struct that looks like this (simplified for concision):

(struct db-chunk (scratchdir-path chunkdir-path) #:mutable)

I'd like to ensure that:

1) At least one of the fields must be set at creation
2) Both fields will accept only a path-string? value
3) Both fields will return string? when accessed, as that makes it
easier to insert into the DB when the time comes.

I can put a #:guard parameter on db-chunk that will check #1 and #2
and (if necessary) transform the input into a string in order to
satisfy #3, but that only works at initialization.  From there I would
need to redefine the mutator:

(struct db-chunk (scratchdir-path chunkdir-path) #:mutable)

(let ([old-mut set-db-chunk-scratchdir-path!])
  (set! set-db-chunk-scratchdir-path!
  (lambda (chnk val)
(old-mut chnk (if (path? val) (path->string val) val))

Alternatively, I could define my struct and then make a bunch of
custom manipulation functions, but then (a) I've got these unguarded
functions floating around and (b) I've given up a major advantage of
structs, which is their concision.


Alternatively again, I could use a class.  That would make it easier
to add new fields if I need to and to add behavior around the
accessors/mutators, but it seems to work against the idea of
functional programming.

What would you more knowledgeable people recommend as the approach here?

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.