* Having Named Parts @ 2022-12-01 11:19 Panicz Maciej Godek 2022-12-01 22:16 ` Per Bothner 0 siblings, 1 reply; 3+ messages in thread From: Panicz Maciej Godek @ 2022-12-01 11:19 UTC (permalink / raw) To: kawa [-- Attachment #1: Type: text/plain, Size: 3706 bytes --] Hi, I've been trying to build a simple wrapper for a hash table, that I call Bundle, such that (Bundle x: 5 y: 10) would return a bundle object, let's call it b, so that I could refer to its fields using b:x and modify them using (set! b:x new-value) And such that it would print itself as (Bundle x: 5 y: 10) and be equal to every other object with the same keys and values. I have found that Kawa offers a gnu.mapping.HasNamedParts interface which facilitates value reference, so that if I define the following class, it lets me refer to the contents of a hash map: (define-simple-class Bundle (gnu.mapping.HasNamedParts) (table ::java.util.Map (java.util.HashMap)) ((get key::String) (table:get key)) ((isConstant key::String)::boolean #f) ((*init*) ;for test purposes (table:put ("x":toString) 5))) (define b ::Bundle (Bundle)) #|kawa|# b:x 5 However, when I invoke (set! b:x 5) Kawa responds with the following error: java.lang.RuntimeException: no such field x in Bundle at gnu.kawa.reflect.SlotSet.apply(SlotSet.java:115) at gnu.kawa.reflect.SlotSet.setField(SlotSet.java:25) at gnu.kawa.functions.SetNamedPart.apply3(SetNamedPart.java:52) at gnu.mapping.Procedure3.applyToObject(Procedure3.java:61) at gnu.mapping.Procedure.applyToConsumerDefault(Procedure.java:75) at gnu.mapping.CallContext.runUntilDone(CallContext.java:586) at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:343) at kawa.Shell.run(Shell.java:300) at kawa.Shell.run(Shell.java:183) at kawa.repl.processArgs(repl.java:724) at kawa.repl.main(repl.java:830) I also saw that there is the gnu.mapping.HasSetter interface, but the getSetter method seems not to be invoked in that context. The source code for the gnu.kawa.functions.SetNamedPart.apply3 method reveals, that there was a plan for supporting this -- the method begins with a commented-out fragment: /* if (container implements HasNamedParts) return ((HasNamedParts) container).getNamedPart(part); */ which wouldn't work with current Kawa, because the HasNamedParts interface doesn't containt getNamedPart method, and instead it contains a method called get. I think that this could be fixed by: - adding a method Object set(String key, Object value) to the HasNamedParts interface - uncommenting and fixing the above snippet to invoke the above method. By the way, the interface also contains isConstant method, which doesn't seem to be documented anywhere. I think that also - for consistency - the interface should also provide a boolean hasPartNamed(String name) method. But this only solves one problem, and there's another one, namely - when I try to initialize Bundle as, say (Bundle x: 5 y: 10) I get the following warnings: /dev/tty:6:20: warning - no field or setter 'x' in class Bundle /dev/tty:6:20: warning - no field or setter 'y' in class Bundle These are issued from the build() method in gnu.kawa.reflect.CompileBuildObject. It does seem that this class doesn't take into account the existence of the HasNamedParts interface at all (but I don't understand it well enough to come up with any idea of fixing it). And the *init* method doesn't seem to accept variable-length arguments. On the other hand, if I wanted to provide a lot of *init* methods like ((*init* k1::gnu.expr.Keyword v1) (table:put (keyword->string k1) v1)) ((*init* k1::gnu.expr.Keyword v1 k2::gnu.expr.Keyword v2) (table:put (keyword->string k1) v1) (table:put (keyword->string k2) v2)) then I'd need to quote the keywords in order to make it work (which isn't something that I want). Is there any way out of this situation? ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Having Named Parts 2022-12-01 11:19 Having Named Parts Panicz Maciej Godek @ 2022-12-01 22:16 ` Per Bothner 2022-12-01 23:15 ` Panicz Maciej Godek 0 siblings, 1 reply; 3+ messages in thread From: Per Bothner @ 2022-12-01 22:16 UTC (permalink / raw) To: Panicz Maciej Godek, kawa On 12/1/22 03:19, Panicz Maciej Godek via Kawa wrote: > I've been trying to build a simple wrapper for a hash table, > that I call Bundle, such that > > (Bundle x: 5 y: 10) > > would return a bundle object, let's call it b, so that I could refer to its > fields using > > b:x That would be a nicer syntax, but there could be some problems combining the concepts of hash-table (extensible with arbitrary keys, not necessarily strings) and structures (with a fixed set of keys). JavaScript does it, and it does make things more convenient. There are a lot of issues to consider. Should key names be strings or symbols? What about keys that are not names (keys or strings)? How does this conflict with existing usage of colon-notation, and how do we deal with those? So I'm not sure if hash tables should implement HasNamedParts by default. An alternative is to use function-call notation, like we do for vectors: (TABLE KEY) (set! (TABLE KEY) VALUE) This has the advantage that it does't conflict with other uses of colon-notation. However, it doesn't provide a constructor syntax "for free". I brought up these concerns on the mailing list years ago. If someone can find these old message it might be helpful ... > I have found that Kawa offers a gnu.mapping.HasNamedParts interface which > facilitates value reference, ...s > The source code for the gnu.kawa.functions.SetNamedPart.apply3 method > reveals, that there was a plan for supporting this -- the method begins > with a commented-out fragment: > > /* > if (container implements HasNamedParts) > return ((HasNamedParts) container).getNamedPart(part); > */ > > which wouldn't work with current Kawa, because the HasNamedParts interface > doesn't containt getNamedPart method, and instead it contains a method > called get. I think that this could be fixed by: > - adding a method Object set(String key, Object value) to the HasNamedParts > interface > - uncommenting and fixing the above snippet to invoke the above method. That is probably OK, except use 'put' instead of 'set' to be consistent with java.util.Map. Also, 'put' needs a default implementation, which should probably throw UnsupportedOperationException. (Defaults were added in Java 8. It is probably OK to require Java 8 as a minimum at this point - though Android might be an issue.) Do compare with the getNamedPart and apply 2 method in GetNamedPart. They handles various extra cases, that might not be appropriate for a setter. > By the way, the interface also contains isConstant method, which doesn't > seem to be documented anywhere. isConstant(KEY) returns true is the binding for KEY is immutable. It is used to optimize namespace resolution. I think that also - for consistency - the > interface should also provide a boolean hasPartNamed(String name) method. > > But this only solves one problem, and there's another one, namely - when I > try to initialize Bundle as, say > > (Bundle x: 5 y: 10) > > I get the following warnings: > > /dev/tty:6:20: warning - no field or setter 'x' in class Bundle > /dev/tty:6:20: warning - no field or setter 'y' in class Bundle > > These are issued from the build() method in > gnu.kawa.reflect.CompileBuildObject. It does seem that this class doesn't > take into account the existence of the HasNamedParts interface at all. That is probably the case. > Is there any way out of this situation? If there was a clean problem-free solution I would have already done so ... That doesn't mean we shouldn't have a better syntax for hash-tables, or that this is a dead end - but there are definitely a number of issues to consider. -- --Per Bothner per@bothner.com http://per.bothner.com/ ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Having Named Parts 2022-12-01 22:16 ` Per Bothner @ 2022-12-01 23:15 ` Panicz Maciej Godek 0 siblings, 0 replies; 3+ messages in thread From: Panicz Maciej Godek @ 2022-12-01 23:15 UTC (permalink / raw) To: Per Bothner; +Cc: kawa [-- Attachment #1: Type: text/plain, Size: 7193 bytes --] czw., 1 gru 2022 o 23:16 Per Bothner <per@bothner.com> napisał(a): > > > On 12/1/22 03:19, Panicz Maciej Godek via Kawa wrote: > > I've been trying to build a simple wrapper for a hash table, > > that I call Bundle, such that > > > > (Bundle x: 5 y: 10) > > > > would return a bundle object, let's call it b, so that I could refer to > its > > fields using > > > > b:x > > That would be a nicer syntax, but there could be some problems combining > the concepts of hash-table (extensible with arbitrary keys, not > necessarily strings) > and structures (with a fixed set of keys). JavaScript does it, and it > does make > things more convenient. > > There are a lot of issues to consider. Should key names be strings or > symbols? > What about keys that are not names (keys or strings)? I think they have to be Strings, because of how the HasNamedPart interface is defined. > How does this conflict > with existing usage of colon-notation, and how do we deal with those? > > So I'm not sure if hash tables should implement HasNamedParts by default. > > No, I don't think so either. But I think it would be nice if Kawa facilitated adding such classes. For the development of GRASP, I have built a macro called define-type <https://github.com/panicz/grasp-android/blob/master/stages/retreat/GRASP/src/define-type.scm>, which is used for defining record-like classes. It is used like this: (define-type (Extent width: real height: real)) And it defines a class that can be instantiated as (define e ::Extent (Extent width: 10 height: 20)) I also have a pattern matcher which supports this notation, so I can write things like (match e ((Extent width: size height: size) 'square) ((Position width: w height: h) 'rectangle)) I like it way more than any SRFI record proposals I've seen. Also, with Kawa's syntax extensions I can write (set! e:width 50) and it Just Works. I find this experience very satisfying (to be honest, I'm quite surprised that many modern languages like Java or Haskell have much worse capabilities of defining structures than the C language). And I thought it would be nice to have a more flexible record-like type that I call Bundle (referring to David Hume's "bundle theory of meaning") -- so that it would also work in the context of pattern matching etc. I don't expect it to be the main interface to hash tables. An alternative is to use function-call notation, like we do for vectors: > (TABLE KEY) > (set! (TABLE KEY) VALUE) > This has the advantage that it does't conflict with other uses of > colon-notation. > However, it doesn't provide a constructor syntax "for free". > > Yes, I actually use that a lot in GRASP. I have defined a bunch of macros such as define-mapping <https://github.com/panicz/grasp-android/blob/master/stages/retreat/GRASP/src/mapping.scm> (which uses a normal hash table underneath) or define-property <https://github.com/panicz/grasp-android/blob/master/stages/retreat/GRASP/src/define-property.scm> (which uses a weak-key table) that is used like this: (define-property (dotted? cell::cons)::boolean #f) The nice thing about this is that it lets me provide the default value very naturally (and I could also signal error for missing keys). I also have a variant of weak-key which can accept more than one argument, that is called define-cache <https://github.com/panicz/grasp-android/blob/master/stages/retreat/GRASP/src/define-cache.scm> (and it's achieved by means of "currying", i.e. constructing hash tables that return hash tables etc.) But the difference between those hash tables and bundles is that bundles are meant to be externalizable, whereas properties are essentially opaque. I brought up these concerns on the mailing list years ago. If someone can > find these old message it might be helpful ... > > > I have found that Kawa offers a gnu.mapping.HasNamedParts interface which > > facilitates value reference, ...s > > The source code for the gnu.kawa.functions.SetNamedPart.apply3 method > > reveals, that there was a plan for supporting this -- the method begins > > with a commented-out fragment: > > > > /* > > if (container implements HasNamedParts) > > return ((HasNamedParts) container).getNamedPart(part); > > */ > > > > which wouldn't work with current Kawa, because the HasNamedParts > interface > > doesn't containt getNamedPart method, and instead it contains a method > > called get. I think that this could be fixed by: > > - adding a method Object set(String key, Object value) to the > HasNamedParts > > interface > > - uncommenting and fixing the above snippet to invoke the above method. > > That is probably OK, except use 'put' instead of 'set' to be consistent > with java.util.Map. > Also, 'put' needs a default implementation, which should probably > throw UnsupportedOperationException. (Defaults were added in Java 8. It > is probably > OK to require Java 8 as a minimum at this point - though Android might be > an issue.) > > Actually Android SDK ships with a tool called d8, which is a replacement for their dx converter, which handles the Java 8 bytecode (I was even able to generate byte code for API level 1 with it) > Do compare with the getNamedPart and apply 2 method in GetNamedPart. > They handles various extra cases, that might not be appropriate for a > setter. > > > By the way, the interface also contains isConstant method, which doesn't > > seem to be documented anywhere. > > isConstant(KEY) returns true is the binding for KEY is immutable. > It is used to optimize namespace resolution. > > I think that also - for consistency - the > > interface should also provide a boolean hasPartNamed(String name) method. > > > > But this only solves one problem, and there's another one, namely - when > I > > try to initialize Bundle as, say > > > > (Bundle x: 5 y: 10) > > > > I get the following warnings: > > > > /dev/tty:6:20: warning - no field or setter 'x' in class Bundle > > /dev/tty:6:20: warning - no field or setter 'y' in class Bundle > > > > These are issued from the build() method in > > gnu.kawa.reflect.CompileBuildObject. It does seem that this class doesn't > > take into account the existence of the HasNamedParts interface at all. > > That is probably the case. > > > Is there any way out of this situation? > > If there was a clean problem-free solution I would have already done so ... > That doesn't mean we shouldn't have a better syntax for hash-tables, > or that this is a dead end - but there are definitely a number of issues > to consider. > > Again, I don't want to push for having an opinionated way of dealing with hash tables. I think it would be sufficient if Kawa could provide their users (or me in particular :D) with the means of creating various different solutions and experimenting with them. I think that supporting setters for HasNamedPart and in CompileBuildObject would simply be consistent with the idea of the HasNamedPart interface, and it doesn't seem to break anything ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2022-12-01 23:15 UTC | newest] Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2022-12-01 11:19 Having Named Parts Panicz Maciej Godek 2022-12-01 22:16 ` Per Bothner 2022-12-01 23:15 ` Panicz Maciej Godek
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).