* anyone define port types? @ 2016-03-28 19:04 Andy Wingo 2016-03-28 23:53 ` Matt Wette ` (4 more replies) 0 siblings, 5 replies; 43+ messages in thread From: Andy Wingo @ 2016-03-28 19:04 UTC (permalink / raw) To: guile-user, guile-devel Hi! I am working on improving our port implementation to take advantage of the opportunity to break ABI in 2.2. I am wondering how much I can break C API as well -- there are some changes that would allow better user-space threading (e.g. http://thread.gmane.org/gmane.lisp.guile.devel/14158/focus=15463 or Chris Webber's 8sync). But those changes would require some incompatible changes to the C API related to port internals. This API is pretty much only used when implementing port types. So, I would like to collect a list of people that implement their own ports :) I know Guile-GNOME does it for GNOME-VFS, though GNOME-VFS is super-old at this point... Anyway. Looking forward to your links :) Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-28 19:04 anyone define port types? Andy Wingo @ 2016-03-28 23:53 ` Matt Wette 2016-04-05 14:06 ` Mark H Weaver 2016-03-29 8:52 ` Nala Ginrut ` (3 subsequent siblings) 4 siblings, 1 reply; 43+ messages in thread From: Matt Wette @ 2016-03-28 23:53 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-user, guile-devel > On Mar 28, 2016, at 12:04 PM, Andy Wingo <wingo@pobox.com> wrote: > I am working on improving our port implementation to take advantage of > the opportunity to break ABI in 2.2. I am wondering how much I can > break C API as well -- there are some changes that would allow better > user-space threading I made an attempt to use soft ports, but backed out after I could not figure a way to extra data I wanted to associate with my port. Addition of an access function to user-defined data would enlarge the cases for which soft ports could be used. Matt ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-28 23:53 ` Matt Wette @ 2016-04-05 14:06 ` Mark H Weaver 2016-04-05 23:55 ` Matt Wette 0 siblings, 1 reply; 43+ messages in thread From: Mark H Weaver @ 2016-04-05 14:06 UTC (permalink / raw) To: Matt Wette; +Cc: Andy Wingo, guile-user, guile-devel Matt Wette <matthew.wette@verizon.net> writes: >> On Mar 28, 2016, at 12:04 PM, Andy Wingo <wingo@pobox.com> wrote: >> I am working on improving our port implementation to take advantage of >> the opportunity to break ABI in 2.2. I am wondering how much I can >> break C API as well -- there are some changes that would allow better >> user-space threading > > I made an attempt to use soft ports, but backed out after I could not > figure a way to extra data I wanted to associate with my port. > > Addition of an access function to user-defined data would enlarge the > cases for which soft ports could be used. Object properties can be used to associate extra information with any object that has identity, which includes ports. See 'make-object-property' in the Guile manual. Mark ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-04-05 14:06 ` Mark H Weaver @ 2016-04-05 23:55 ` Matt Wette 2016-06-11 16:50 ` Andy Wingo 0 siblings, 1 reply; 43+ messages in thread From: Matt Wette @ 2016-04-05 23:55 UTC (permalink / raw) To: guile-devel, guile-user > On Apr 5, 2016, at 7:06 AM, Mark H Weaver <mhw@netris.org> wrote: > > Matt Wette <matthew.wette@verizon.net> writes: > >>> On Mar 28, 2016, at 12:04 PM, Andy Wingo <wingo@pobox.com> wrote: >>> I am working on improving our port implementation to take advantage of >>> the opportunity to break ABI in 2.2. I am wondering how much I can >>> break C API as well -- there are some changes that would allow better >>> user-space threading >> >> I made an attempt to use soft ports, but backed out after I could not >> figure a way to extra data I wanted to associate with my port. >> >> Addition of an access function to user-defined data would enlarge the >> cases for which soft ports could be used. > > Object properties can be used to associate extra information with any > object that has identity, which includes ports. See > 'make-object-property' in the Guile manual. Thanks. I had forgotten about that. — Matt ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-04-05 23:55 ` Matt Wette @ 2016-06-11 16:50 ` Andy Wingo 0 siblings, 0 replies; 43+ messages in thread From: Andy Wingo @ 2016-06-11 16:50 UTC (permalink / raw) To: Matt Wette; +Cc: guile-user, guile-devel On Wed 06 Apr 2016 01:55, Matt Wette <matthew.wette@verizon.net> writes: >> On Apr 5, 2016, at 7:06 AM, Mark H Weaver <mhw@netris.org> wrote: >> >> Matt Wette <matthew.wette@verizon.net> writes: >> >>>> On Mar 28, 2016, at 12:04 PM, Andy Wingo <wingo@pobox.com> wrote: >>>> I am working on improving our port implementation to take advantage of >>>> the opportunity to break ABI in 2.2. I am wondering how much I can >>>> break C API as well -- there are some changes that would allow better >>>> user-space threading >>> >>> I made an attempt to use soft ports, but backed out after I could not >>> figure a way to extra data I wanted to associate with my port. >>> >>> Addition of an access function to user-defined data would enlarge the >>> cases for which soft ports could be used. >> >> Object properties can be used to associate extra information with any >> object that has identity, which includes ports. See >> 'make-object-property' in the Guile manual. > > Thanks. I had forgotten about that. — Matt I think for soft ports (and custom binary i/o ports) the idea is actually that the "handler" procedures are actually closures. I.e. (let ((my-obj ...)) (make-soft-port (vector (lambda (c) (do-write my-obj c)) ...) modes)) No need for extra arguments if you have closures. Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-28 19:04 anyone define port types? Andy Wingo 2016-03-28 23:53 ` Matt Wette @ 2016-03-29 8:52 ` Nala Ginrut 2016-03-30 6:29 ` Panicz Maciej Godek ` (2 subsequent siblings) 4 siblings, 0 replies; 43+ messages in thread From: Nala Ginrut @ 2016-03-29 8:52 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-user, guile-devel On Mon, 2016-03-28 at 21:04 +0200, Andy Wingo wrote: > Hi! > > I am working on improving our port implementation to take advantage of > the opportunity to break ABI in 2.2. I am wondering how much I can > break C API as well -- there are some changes that would allow better > user-space threading > (e.g. http://thread.gmane.org/gmane.lisp.guile.devel/14158/focus=15463 > or Chris Webber's 8sync). But those changes would require some > incompatible changes to the C API related to port internals. This API > is pretty much only used when implementing port types. So, I would like > to collect a list of people that implement their own ports :) I know > Guile-GNOME does it for GNOME-VFS, though GNOME-VFS is super-old at this > point... Anyway. Looking forward to your links :) Fortunately, I haven't do the work for the port, but it's likely to customize the soft port in the future. If so, I would choose to stick to 2.1+ Best regards. ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-28 19:04 anyone define port types? Andy Wingo 2016-03-28 23:53 ` Matt Wette 2016-03-29 8:52 ` Nala Ginrut @ 2016-03-30 6:29 ` Panicz Maciej Godek 2016-03-30 11:18 ` Jan Nieuwenhuizen 2016-06-11 16:53 ` Andy Wingo 2016-04-01 14:38 ` Ludovic Courtès 2016-04-14 14:08 ` Ludovic Courtès 4 siblings, 2 replies; 43+ messages in thread From: Panicz Maciej Godek @ 2016-03-30 6:29 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-user@gnu.org, guile-devel [-- Attachment #1: Type: text/plain, Size: 455 bytes --] Hi Andy, I have been using soft ports to implement a text widget in my GUI framework. I also used GOOPS, which I regret to this day, and so the whole framework needs a serious rewrite, but if you're collecting various species to the museum of make-soft-port, you can have a look: https://bitbucket.org/panicz/slayer/src/8b741c21b7372c24874d7cd1875e2aae3fc43abe/guile-modules/widgets/text-area.scm?at=default&fileviewer=file-view-default Regards, Panicz [-- Attachment #2: Type: text/html, Size: 808 bytes --] ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 6:29 ` Panicz Maciej Godek @ 2016-03-30 11:18 ` Jan Nieuwenhuizen 2016-03-30 17:17 ` Panicz Maciej Godek 2016-06-11 16:53 ` Andy Wingo 1 sibling, 1 reply; 43+ messages in thread From: Jan Nieuwenhuizen @ 2016-03-30 11:18 UTC (permalink / raw) To: Panicz Maciej Godek; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel Panicz Maciej Godek writes: > I also used GOOPS, which I regret to this day, and so the > whole framework needs a serious rewrite What is it that you do not like about GOOPS? I have a project that may be in the same position. I used GOOPS initially, then rewrote it to use either GOOPS, plain lists or records backends. When I kind of decided to go for plain lists, I discovered people (eg, guix) are using records; and meanwhile Andy has made GOOPS perform. Now I'm thinking: if you only use GOOPS as a friendlier way to create records, that may be nice after all. Just wondering. Greetings, Jan -- Jan Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond http://lilypond.org Freelance IT http://JoyofSource.com | Avatar® http://AvatarAcademy.nl ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 11:18 ` Jan Nieuwenhuizen @ 2016-03-30 17:17 ` Panicz Maciej Godek 2016-03-30 17:53 ` Marko Rauhamaa 0 siblings, 1 reply; 43+ messages in thread From: Panicz Maciej Godek @ 2016-03-30 17:17 UTC (permalink / raw) To: Jan Nieuwenhuizen; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel [-- Attachment #1: Type: text/plain, Size: 800 bytes --] 2016-03-30 13:18 GMT+02:00 Jan Nieuwenhuizen <janneke@gnu.org>: > Panicz Maciej Godek writes: > > > I also used GOOPS, which I regret to this day, and so the > > whole framework needs a serious rewrite > > What is it that you do not like about GOOPS? Most specifically, I dislike its middle three letters. The problem with OOP is that it requires to know exactly what ones want -- it is difficult to change the design of your program after it's been written (and it is also difficult to come up with a good design from the beginning), and -- since it is based on state mutation - it makes it difficult to reason about your program. On the practical side, it was a bit counterintuitive that <uvec> and <vector> were unrelated, and I think that there were some issues with <pair> and <list> types. [-- Attachment #2: Type: text/html, Size: 1202 bytes --] ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 17:17 ` Panicz Maciej Godek @ 2016-03-30 17:53 ` Marko Rauhamaa 2016-03-30 19:02 ` Panicz Maciej Godek 2016-03-30 19:43 ` Jan Wedekind 0 siblings, 2 replies; 43+ messages in thread From: Marko Rauhamaa @ 2016-03-30 17:53 UTC (permalink / raw) To: Panicz Maciej Godek; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel Panicz Maciej Godek <godek.maciek@gmail.com>: > 2016-03-30 13:18 GMT+02:00 Jan Nieuwenhuizen <janneke@gnu.org>: > >> Panicz Maciej Godek writes: >> >> > I also used GOOPS, which I regret to this day, and so the >> > whole framework needs a serious rewrite >> >> What is it that you do not like about GOOPS? > > Most specifically, I dislike its middle three letters. The problem > with OOP is that it requires to know exactly what ones want -- it is > difficult to change the design of your program after it's been written > (and it is also difficult to come up with a good design from the > beginning), and -- since it is based on state mutation - it makes it > difficult to reason about your program. > > On the practical side, it was a bit counterintuitive that <uvec> and > <vector> were unrelated, and I think that there were some issues with > <pair> and <list> types. I like OOP, only I don't like GOOPS. Its classes and generic functions seem so idiomatically out of place, unschemish, if you will. This is how OOP ought to be done: <URL: https://www.gnu.org/software/guile/manual/html_node/OO-Closure.htm l#OO-Closure> I have created a tiny Guile module ("simpleton") that generalizes the principle. In particular, * You don't need classes for OOP. You only need objects. * Do tie methods to objects. Don't pretend methods are external to objects. * Don't expose the internal state of objects. Only interact with the object through methods. Marko ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 17:53 ` Marko Rauhamaa @ 2016-03-30 19:02 ` Panicz Maciej Godek 2016-03-30 19:57 ` Marko Rauhamaa 2016-03-30 19:43 ` Jan Wedekind 1 sibling, 1 reply; 43+ messages in thread From: Panicz Maciej Godek @ 2016-03-30 19:02 UTC (permalink / raw) To: Marko Rauhamaa; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel [-- Attachment #1: Type: text/plain, Size: 1600 bytes --] 2016-03-30 19:53 GMT+02:00 Marko Rauhamaa <marko@pacujo.net>: > I like OOP, only I don't like GOOPS. Its classes and generic functions > seem so idiomatically out of place, unschemish, if you will. > > This is how OOP ought to be done: > > <URL: https://www.gnu.org/software/guile/manual/html_node/OO-Closure.htm > l#OO-Closure> > > The problem with closures is, among others, that they are non-serializable (I think that Termite solves some of those issues gracefully) I have created a tiny Guile module ("simpleton") that generalizes the > principle. In particular, > > * You don't need classes for OOP. You only need objects. > > JavaScript made a similar assumption, which -- I believe -- turned out cumbersome, because I sometimes have to pretend that it has classes despite that it doesn't. > * Do tie methods to objects. Don't pretend methods are external to > objects. * Don't expose the internal state of objects. Only interact with the > object through methods. I think it is a good rule, but it's better if you can do without state. I believe that methods *are* external to objects. Linguistically, you don't "button.press()"; you "press(button)". I also think that tying methods to objects is one of the problems of OOP, because the designer of an object has to know in advance which actions on an object are conceivable. Perhaps certain devices, like tape recorders, are modelled well within OOP -- you have a well-defined interface (like the play, rewind, fast-forward button) and a hidden internal state. But I'm afraid that this metaphor doesn't scale well. [-- Attachment #2: Type: text/html, Size: 2875 bytes --] ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 19:02 ` Panicz Maciej Godek @ 2016-03-30 19:57 ` Marko Rauhamaa 2016-03-31 16:11 ` Barry Fishman 0 siblings, 1 reply; 43+ messages in thread From: Marko Rauhamaa @ 2016-03-30 19:57 UTC (permalink / raw) To: Panicz Maciej Godek; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel Panicz Maciej Godek <godek.maciek@gmail.com>: > 2016-03-30 19:53 GMT+02:00 Marko Rauhamaa <marko@pacujo.net>: > The problem with closures is, among others, that they are > non-serializable What is there to serialize in objects? How do you serialize a car? How do you serialize an ant? How do you serialize a person? All you can serialize is information. Objects are living things we experience through interactions alone. And yet, UNIX knows how to suspend a process. Virtualization technologies are very good at snapshotting live virtual machines. Tricky business but the results are quite impressive albeit not fully flawless. >> * You don't need classes for OOP. You only need objects. > > JavaScript made a similar assumption, which -- I believe -- turned out > cumbersome, because I sometimes have to pretend that it has classes > despite that it doesn't. I can't speak for JavaScript. Point is, you should only care about interfacing the objects, not about their pedigree. >> * Don't expose the internal state of objects. Only interact with the >> object through methods. > > I think it is a good rule, but it's better if you can do without > state. Stateless objects are dead as stone. However you present it, statefulness is the core of objects. > I believe that methods *are* external to objects. Linguistically, you > don't "button.press()"; you "press(button)". I won't debate linguistics. I can only say the methods-of-objects is a very convenient paradigm that helps me organize practical programs. > I also think that tying methods to objects is one of the problems of > OOP, because the designer of an object has to know in advance which > actions on an object are conceivable. You mean you can't associate new methods to an object. That's true and can be annoying sometimes. However, GOOPS' cure is worse than the disease: it exposes the slots of the object. Marko ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 19:57 ` Marko Rauhamaa @ 2016-03-31 16:11 ` Barry Fishman 2016-03-31 19:28 ` Marko Rauhamaa 0 siblings, 1 reply; 43+ messages in thread From: Barry Fishman @ 2016-03-31 16:11 UTC (permalink / raw) To: guile-user; +Cc: guile-devel On 2016-03-30 22:57:25 +0300, Marko Rauhamaa wrote: > Panicz Maciej Godek <godek.maciek@gmail.com>: > >> 2016-03-30 19:53 GMT+02:00 Marko Rauhamaa <marko@pacujo.net>: >> The problem with closures is, among others, that they are >> non-serializable > > What is there to serialize in objects? How do you serialize a car? How > do you serialize an ant? How do you serialize a person? > > All you can serialize is information. Objects are living things we > experience through interactions alone. Do we really want our computers to behave like organic black boxes? I think is is more limiting to look at Objects as organic entities rather than encapsulation of state. If objects are state they can be recorded. When your bank account balance looks wrong its good to be able to replay transactions to find out what you are missing. When transactions are happening across a network its nice to be able to restart them rather deal with lost transactions or single points of failure. Yes, Objects can produce their own logs, but this becomes more code to debug rather than a better debugging tool. And the act of building such an interface, really establishes them as state. It is nice to think about the fact that a single implementation of that state is not required by a design, or that it can be changed with little impact on the rest of the system. But such implementation flexibility is not dependent on a "methods within object" type of object construction. It is more a matter of what is exported at the module level. In Guile you can export, at the module level, what interface you like and hide any raw "get-x" style assessors. True you can get at raw state through the slot interface, but Guile is built around observably, not security, and you could get at the contents of closures if you really wanted to. The fact that objects represent state doe not force the exact implementation of that state be considered outside of a single module. > And yet, UNIX knows how to suspend a process. Virtualization > technologies are very good at snapshotting live virtual machines. Tricky > business but the results are quite impressive albeit not fully > flawless. And a crude way to try to deal with this. Serialization does not have to be lost. I used to do large scale development in C++. Books like "Design Patterns" are really just series of hacks to get somewhat around the fact that objects built around a fixed, rigidly defined set of methods becomes difficult to adapt and maintain as projects grow over time. I found that Common Lisp's CLOS style way of looking at objects would have made thing far easier to develop and maintain and produce a more stable code base. I really got to dislike C++'s and Java's object model. With distributed environments and the more functional way of looking at things that are possible in Scheme, I think we would better off allowing for extending objects by treating them as immutable state and using monads to perform actions, rather than just allowing for mutable objects and methods. Monads don't constrain object state implementation either. I don't mean this to be construed as trying to make Guile a purely functional language. I like choices. >> I also think that tying methods to objects is one of the problems of >> OOP, because the designer of an object has to know in advance which >> actions on an object are conceivable. > > You mean you can't associate new methods to an object. That's true and > can be annoying sometimes. However, GOOPS' cure is worse than the > disease: it exposes the slots of the object. What is exposed or not exposed seems to be something better handled by what modules export, and not be intrinsic to objects themselves. This allows methods, defined outside the module, that uses just the module interface to the object (or several objects) to be easily defined and somewhat separately manged. This becomes crucial as systems grow and change. Especially allowing for the idea of multi-methods. One of the security issues with Apple's Objective C is that object methods are always available, if you know about them. Closure based interfaces do make hiding of state easier, but I think localizing all method code in one place makes complex large systems more difficult to maintain. Of course at the true security level, both can be hacked. We are dealing more with what developers might presume about an interface rather a malicious act. Although Scheme itself is not the usual place to think about complex systems, I think Guile does, given what is currently being implemented. -- Barry Fishman ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-31 16:11 ` Barry Fishman @ 2016-03-31 19:28 ` Marko Rauhamaa 0 siblings, 0 replies; 43+ messages in thread From: Marko Rauhamaa @ 2016-03-31 19:28 UTC (permalink / raw) To: Barry Fishman; +Cc: guile-user, guile-devel Barry Fishman <barry@ecubist.org>: > On 2016-03-30 22:57:25 +0300, Marko Rauhamaa wrote: >> All you can serialize is information. Objects are living things we >> experience through interactions alone. > > Do we really want our computers to behave like organic black boxes? I'll say yes. > In Guile you can export, at the module level, what interface you like > and hide any raw "get-x" style assessors. You mean GOOPS. Guile can do a lot better. > True you can get at raw state through the slot interface, but Guile is > built around observably, not security, and you could get at the > contents of closures if you really wanted to. I don't care about security. I care about the approach. To me, objects are thingies that interact with their surroundings. GOOPS' model is the opposite: objects are conceptually mere dead records, structs. > The fact that objects represent state doe not force the > exact implementation of that state be considered outside of a single > module. The accessor (get-x) evokes the idea that there's a piece of information guarded by the object that the object is willing to divulge through the accessor, however implicitly that piece of information is encoded internally. I don't usually think of objects as containing information. I don't like Java's and Python's properties because they also make objects seem like records. > Serialization does not have to be lost. You serialize data, you don't serialize objects. You can serialize configuration information. You can serialize messages. You can serialize collected data. But objects exist, live, breathe and interact. > I used to do large scale development in C++. Books like "Design > Patterns" are really just series of hacks to get somewhat around the > fact that objects built around a fixed, rigidly defined set of methods > becomes difficult to adapt and maintain as projects grow over time. I have some development on my belt as well. I don't know what trauma design patterns might have inflicted on you on your career. > I found that Common Lisp's CLOS style way of looking at objects would > have made thing far easier to develop and maintain and produce a more > stable code base. I really got to dislike C++'s and Java's object > model. So you haven't personally experienced the blessings of CLOS? > I don't mean this to be construed as trying to make Guile a purely > functional language. I like choices. GOOPS is the antithesis of functional programming, I think. >> You mean you can't associate new methods to an object. That's true >> and can be annoying sometimes. However, GOOPS' cure is worse than the >> disease: it exposes the slots of the object. > > What is exposed or not exposed seems to be something better handled by > what modules export, and not be intrinsic to objects themselves. This > allows methods, defined outside the module, that uses just the module > interface to the object (or several objects) to be easily defined and > somewhat separately manged. This becomes crucial as systems grow and > change. Especially allowing for the idea of multi-methods. External, derived functionality is possible at that level with the classic rigid methods as well. If the external methods depend on the methods the module chooses to expose, you are back with the classic model you are trying to avoid. Marko ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 17:53 ` Marko Rauhamaa 2016-03-30 19:02 ` Panicz Maciej Godek @ 2016-03-30 19:43 ` Jan Wedekind 2016-03-30 20:07 ` Marko Rauhamaa 1 sibling, 1 reply; 43+ messages in thread From: Jan Wedekind @ 2016-03-30 19:43 UTC (permalink / raw) To: Marko Rauhamaa; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel On Wed, 30 Mar 2016, Marko Rauhamaa wrote: > Panicz Maciej Godek <godek.maciek@gmail.com>: > >> 2016-03-30 13:18 GMT+02:00 Jan Nieuwenhuizen <janneke@gnu.org>: >> >>> Panicz Maciej Godek writes: >>> >>>> I also used GOOPS, which I regret to this day, and so the >>>> whole framework needs a serious rewrite >>> >>> What is it that you do not like about GOOPS? >> >> Most specifically, I dislike its middle three letters. The problem >> with OOP is that it requires to know exactly what ones want -- it is >> difficult to change the design of your program after it's been written >> (and it is also difficult to come up with a good design from the >> beginning), and -- since it is based on state mutation - it makes it >> difficult to reason about your program. >> >> On the practical side, it was a bit counterintuitive that <uvec> and >> <vector> were unrelated, and I think that there were some issues with >> <pair> and <list> types. > > I like OOP, only I don't like GOOPS. Its classes and generic functions > seem so idiomatically out of place, unschemish, if you will. > > This is how OOP ought to be done: > > <URL: https://www.gnu.org/software/guile/manual/html_node/OO-Closure.htm > l#OO-Closure> > > I have created a tiny Guile module ("simpleton") that generalizes the > principle. In particular, > > * You don't need classes for OOP. You only need objects. > > * Do tie methods to objects. Don't pretend methods are external to > objects. > > * Don't expose the internal state of objects. Only interact with the > object through methods. > > > Marko GOOPS supports "open" classes and multiple-dispatch. E.g. you can extend the "write" method to control how an object is displayed within the Guile REPL [1]. Another interesting approach are multi-methods in Clojure which don't even require explicit types for dispatching. [1] http://wedesoft.de/oop-with-goops.html ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 19:43 ` Jan Wedekind @ 2016-03-30 20:07 ` Marko Rauhamaa 2016-03-30 21:01 ` Jan Wedekind 0 siblings, 1 reply; 43+ messages in thread From: Marko Rauhamaa @ 2016-03-30 20:07 UTC (permalink / raw) To: Jan Wedekind; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel Jan Wedekind <jan@wedesoft.de>: > GOOPS supports "open" classes and multiple-dispatch. E.g. you can extend > the "write" method to control how an object is displayed within the > Guile REPL [1]. Another interesting approach are multi-methods in > Clojure which don't even require explicit types for dispatching. > > [1] http://wedesoft.de/oop-with-goops.html Your example demonstrates my problem with GOOPS: you are redefining (display) by penetrating the black box with (slot-ref). What would happen to your method if I changed the implementation of <a> so it no longer has the slot x, even virtually. GOOPS' has the worst possible object model: objects are seen as mere data records. The concept of a "slot" is an anathema to OOP. Think of a UNIX file. You access it through open(2), write(2), read(2), close(2) and so on. You don't know and you shouldn't care how the file contents are organized on the disk (if there is a disk!). GOOPS would like to abandon the system calls and just expose the bytes (slots) on the disk. Then, GOOPS lets you define any kind of system calls you see fit. Marko ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 20:07 ` Marko Rauhamaa @ 2016-03-30 21:01 ` Jan Wedekind 2016-03-30 22:44 ` Marko Rauhamaa 0 siblings, 1 reply; 43+ messages in thread From: Jan Wedekind @ 2016-03-30 21:01 UTC (permalink / raw) To: Marko Rauhamaa; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel On Wed, 30 Mar 2016, Marko Rauhamaa wrote: > Jan Wedekind <jan@wedesoft.de>: > >> GOOPS supports "open" classes and multiple-dispatch. E.g. you can extend >> the "write" method to control how an object is displayed within the >> Guile REPL [1]. Another interesting approach are multi-methods in >> Clojure which don't even require explicit types for dispatching. >> >> [1] http://wedesoft.de/oop-with-goops.html > > Your example demonstrates my problem with GOOPS: you are redefining > (display) by penetrating the black box with (slot-ref). What would > happen to your method if I changed the implementation of <a> so it > no longer has the slot x, even virtually. > > GOOPS' has the worst possible object model: objects are seen as mere > data records. The concept of a "slot" is an anathema to OOP. > > Think of a UNIX file. You access it through open(2), write(2), read(2), > close(2) and so on. You don't know and you shouldn't care how the file > contents are organized on the disk (if there is a disk!). GOOPS would > like to abandon the system calls and just expose the bytes (slots) on > the disk. Then, GOOPS lets you define any kind of system calls you see > fit. > > > Marko Ok, I have updated the example to use accessor functions instead of "slot-ref". But yes, a GOOPS generic only dispatches on types where instances in turn need to define slots (attributes). That's why I pointed out Clojure multimethods which uses a more flexible dispatch mechanism (using an inheritance graph and dispatching on the return value of an arbitrary function). GOOPS (as well as Clojure multimethods) provide "open" classes and multiple-dispatch. Two things which I can really recommend to play with. ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 21:01 ` Jan Wedekind @ 2016-03-30 22:44 ` Marko Rauhamaa 2016-03-31 20:42 ` Jan Wedekind 0 siblings, 1 reply; 43+ messages in thread From: Marko Rauhamaa @ 2016-03-30 22:44 UTC (permalink / raw) To: Jan Wedekind; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel Jan Wedekind <jan@wedesoft.de>: > On Wed, 30 Mar 2016, Marko Rauhamaa wrote: >> GOOPS' has the worst possible object model: objects are seen as mere >> data records. The concept of a "slot" is an anathema to OOP. > > Ok, I have updated the example to use accessor functions instead of > "slot-ref". (get-x) is only a fig leaf for (slot-ref). In general, no user of an <a> object should think the object holds a piece of information called x. Instead, you should be interacting with the abstract object <a>. Python people call it duck-typing. Java, Go et al use interfaces. Even C can do opaque structs. C++ suffers from "private" data members, and GOOPS strips away even that thin veil. Remember: [...] during her journey south to be married, the young queen-to-be passed through a town that was famed for its silk stockings, then rare and expensive items. Wishing to show her due courtesy, the merchants of the town offered to present her with a pair. [...] The Queen’s courtiers were aghast at this embarrassing breach of decorum, regarded as both indecent and audacious, and one replied loftily that “The Queen of Spain has no legs”. <URL: http://www.worldwidewords.org/qa/qa-que2.htm> Marko ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 22:44 ` Marko Rauhamaa @ 2016-03-31 20:42 ` Jan Wedekind 2016-03-31 22:28 ` Marko Rauhamaa 0 siblings, 1 reply; 43+ messages in thread From: Jan Wedekind @ 2016-03-31 20:42 UTC (permalink / raw) To: Marko Rauhamaa; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel On Thu, 31 Mar 2016, Marko Rauhamaa wrote: > Jan Wedekind <jan@wedesoft.de>: > >> On Wed, 30 Mar 2016, Marko Rauhamaa wrote: >>> GOOPS' has the worst possible object model: objects are seen as mere >>> data records. The concept of a "slot" is an anathema to OOP. >> >> Ok, I have updated the example to use accessor functions instead of >> "slot-ref". > > (get-x) is only a fig leaf for (slot-ref). In general, no user of an <a> > object should think the object holds a piece of information called x. > Instead, you should be interacting with the abstract object <a>. Well, actually (get-x) is a generic as well. I.e. it is polymorphic and does not have to be a simple accessor for a slot. I.e. the accessor syntax is just a shortcut for declaring a generic for accessing a slot separately: (use-modules (oop goops)) (define-class <a> () (x #:init-keyword #:x)) (define-method (get-x (self <a>)) (slot-ref self 'x)) "get-x" can be defined differently for each class and its implementation can be doing something different than accessing a slot. > > Python people call it duck-typing. > > Java, Go et al use interfaces. > > Even C can do opaque structs. > > C++ suffers from "private" data members, and GOOPS strips away even that > thin veil. Here is an example using duck-typing with the generic "get-x": (define-class <b> ()) (define-method (get-x (self <b>)) 123) (define-method (use-duck-typing (x <object>)) (get-x x)) (use-duck-typing (make <a> #:x 3)) ; => 3 (use-duck-typing (make <b>)) ; => 123 ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-31 20:42 ` Jan Wedekind @ 2016-03-31 22:28 ` Marko Rauhamaa 0 siblings, 0 replies; 43+ messages in thread From: Marko Rauhamaa @ 2016-03-31 22:28 UTC (permalink / raw) To: Jan Wedekind; +Cc: Andy Wingo, guile-user@gnu.org, guile-devel Jan Wedekind <jan@wedesoft.de>: > On Thu, 31 Mar 2016, Marko Rauhamaa wrote: >> (get-x) is only a fig leaf for (slot-ref). In general, no user of an >> <a> object should think the object holds a piece of information >> called x. Instead, you should be interacting with the abstract object >> <a>. > > Well, actually (get-x) is a generic as well. I.e. it is polymorphic > and does not have to be a simple accessor for a slot. Naturally, that's not my point. In practice, GOOPS can be used just like any classic object system. However, * The (make)/(initialize) mechanism is strongly tied to the slots. There is a strong temptation to define a nongeneric constructor function for each class that internally calls (make) with appropriate slot settings. This is suggested at the bottom of <URL: https://www.gn u.org/software/guile/manual/html_node/Slot-Description-Example.html#Sl ot-Description-Example>. Unfortunately, the separate constructor can't invoke a base class's constructor function but must contend with (make), which exposes the base class's slots to the derived class. Ideally, you should be able to interpret the keyword args to (make)/(initialize) independently of the slots. However, the (next-method) mechanism makes this tricky. * The (self <class>) notation makes code look very un-Schemey and noisy. For example, classic port dispatching is handled with the assumption that the port knows its methods. * The slot emphasis permeates the documentation and available facilities. The long <my-complex> is advertised as "a better solution" even though it is extremely noisy and complex numbers are extremely passive as objects (<URL: https://www.gnu.org/software/gui le/manual/html_node/Slot-Description-Example.html#Slot-Description-E xample>). What on earth are (shallow-clone) and (deep-clone) supposed to accomplish? Duplicate the database? Open another session to the server? Marko ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-30 6:29 ` Panicz Maciej Godek 2016-03-30 11:18 ` Jan Nieuwenhuizen @ 2016-06-11 16:53 ` Andy Wingo 1 sibling, 0 replies; 43+ messages in thread From: Andy Wingo @ 2016-06-11 16:53 UTC (permalink / raw) To: Panicz Maciej Godek; +Cc: guile-user@gnu.org, guile-devel On Wed 30 Mar 2016 08:29, Panicz Maciej Godek <godek.maciek@gmail.com> writes: > Hi Andy, > I have been using soft ports to implement a text widget in my GUI > framework. I also used GOOPS, which I regret to this day, and so the > whole framework needs a serious rewrite, but if you're collecting > various species to the museum of make-soft-port, you can have a look: > > https://bitbucket.org/panicz/slayer/src/8b741c21b7372c24874d7cd1875e2aae3fc43abe/guile-modules/widgets/text-area.scm?at=default&fileviewer=file-view-default Thanks. The interface to soft ports hasn't changed FWIW; nothing has changed from a Scheme programmer's perspective, except that all ports can have buffering now. See: http://www.gnu.org/software/guile/docs/master/guile.html/Buffering.html Cheers, Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-28 19:04 anyone define port types? Andy Wingo ` (2 preceding siblings ...) 2016-03-30 6:29 ` Panicz Maciej Godek @ 2016-04-01 14:38 ` Ludovic Courtès 2016-06-11 16:57 ` Andy Wingo 2016-04-14 14:08 ` Ludovic Courtès 4 siblings, 1 reply; 43+ messages in thread From: Ludovic Courtès @ 2016-04-01 14:38 UTC (permalink / raw) To: guile-devel; +Cc: guile-user Hello! Here’s a port type! :-) https://gitlab.com/gnutls/gnutls/blob/master/guile/src/core.c#L785 Ludo’. ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-04-01 14:38 ` Ludovic Courtès @ 2016-06-11 16:57 ` Andy Wingo 0 siblings, 0 replies; 43+ messages in thread From: Andy Wingo @ 2016-06-11 16:57 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-user, guile-devel On Fri 01 Apr 2016 16:38, ludo@gnu.org (Ludovic Courtès) writes: > Here’s a port type! :-) > > https://gitlab.com/gnutls/gnutls/blob/master/guile/src/core.c#L785 Thanks! So port types written in C will have to change unfortunately :/ From NEWS: ** Complete overhaul of port internals Guile's ports have been completely overhauled to allow Guile developers and eventually Guile users to write low-level input and output routines in Scheme. The new internals will eventually allow for user-space tasklets or green threads that suspend to a scheduler when they would cause blocking I/O, allowing users to write straightforward network services that parse their input and send their output as if it were blocking, while under the hood Guile can multiplex many active connections at once. At the same time, this change makes Guile's ports implementation much more maintainable, rationalizing the many legacy port internals and making sure that the abstractions between the user, Guile's core ports facility, and the port implementations result in a system that is as performant and expressive as possible. The interface to the user has no significant change, neither on the C side nor on the Scheme side. However this refactoring has changed the interface to the port implementor in an incompatible way. See below for full details. And later: ** API to define new port types from C has changed See the newly expanded "I/O Extensions" in the manual, for full details. Notably: *** Remove `scm_set_port_mark' Port mark functions have not been called since the switch to the BDW garbage collector. *** Remove `scm_set_port_equalp' Likewise port equal functions weren't being called. Given that ports have their own internal buffers, it doesn't make sense to hook them into equal? anyway. *** Remove `scm_set_port_free' It used to be that if an open port became unreachable, a special "free" function would be called instead of the "close" function. Now that the BDW-GC collector allows us to run arbitrary code in finalizers, we can simplify to just call "close" on the port and remove the separate free functions. Note that hooking into the garbage collector has some overhead. For that reason Guile exposes a new interface, `scm_set_port_needs_close_on_gc', allowing port implementations to indicate to Guile whether they need closing on GC or not. *** Remove `scm_set_port_end_input', `scm_set_port_flush' As buffering is handled by Guile itself, these functions which were to manage an implementation-side buffer are no longer needed. *** Change prototype of `scm_make_port_type' The `read' (renamed from `fill_input') and `write' functions now operate on bytevectors. Also the `mode_bits' argument now inplicitly includes SCM_OPN, so you don't need to include these. *** Change prototype of port `close' function The port close function now returns void. *** Port and port type data structures are now opaque Port type implementations should now use API to access port state. However, since the change to handle port buffering centrally, port type implementations rarely need to access unrelated port state. *** Port types are now `scm_t_port_type*', not a tc16 value `scm_make_port_type' now returns an opaque pointer, not a tc16. Relatedly, the limitation that there only be 256 port types has been lifted. Now. How to adapt? You can do the #if thing with versions. Or, maybe we should flesh out the custom binary input/output port support so that you can implement a GnuTLS port in Scheme. WDYT? Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-03-28 19:04 anyone define port types? Andy Wingo ` (3 preceding siblings ...) 2016-04-01 14:38 ` Ludovic Courtès @ 2016-04-14 14:08 ` Ludovic Courtès 2016-06-11 17:02 ` Andy Wingo 4 siblings, 1 reply; 43+ messages in thread From: Ludovic Courtès @ 2016-04-14 14:08 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-user, guile-devel Hey! Andy Wingo <wingo@pobox.com> skribis: > I am working on improving our port implementation to take advantage of > the opportunity to break ABI in 2.2. I am wondering how much I can > break C API as well -- there are some changes that would allow better > user-space threading > (e.g. http://thread.gmane.org/gmane.lisp.guile.devel/14158/focus=15463 > or Chris Webber's 8sync). But those changes would require some > incompatible changes to the C API related to port internals. This API > is pretty much only used when implementing port types. So, I would like > to collect a list of people that implement their own ports :) I know > Guile-GNOME does it for GNOME-VFS, though GNOME-VFS is super-old at this > point... Anyway. Looking forward to your links :) What do you conclude from this poll? :-) From what you’ve seen, how do you think current uses would impact the refactoring work (and vice versa)? Thanks, Ludo’. ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-04-14 14:08 ` Ludovic Courtès @ 2016-06-11 17:02 ` Andy Wingo 2016-06-12 8:25 ` Chris Vine 0 siblings, 1 reply; 43+ messages in thread From: Andy Wingo @ 2016-06-11 17:02 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-user, guile-devel On Thu 14 Apr 2016 16:08, ludo@gnu.org (Ludovic Courtès) writes: > Andy Wingo <wingo@pobox.com> skribis: > >> I am working on improving our port implementation to take advantage of >> the opportunity to break ABI in 2.2. I am wondering how much I can >> break C API as well -- there are some changes that would allow better >> user-space threading >> (e.g. http://thread.gmane.org/gmane.lisp.guile.devel/14158/focus=15463 >> or Chris Webber's 8sync). But those changes would require some >> incompatible changes to the C API related to port internals. This API >> is pretty much only used when implementing port types. So, I would like >> to collect a list of people that implement their own ports :) I know >> Guile-GNOME does it for GNOME-VFS, though GNOME-VFS is super-old at this >> point... Anyway. Looking forward to your links :) > > What do you conclude from this poll? :-) > > From what you’ve seen, how do you think current uses would impact the > refactoring work (and vice versa)? Sorry for the late response :) My conclusion is that we should not change anything about the Scheme interface, but that with close communication with C port hackers, we can feel OK about changing the C interface to make it both more simple and more expressive. Since libguile is parallel-installable and you have to select the version of your Guile when you build your project, of course people will be able to update / upgrade when they choose to. I put in a lot of effort to the documentation; check it out: http://www.gnu.org/software/guile/docs/master/guile.html/Input-and-Output.html Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-11 17:02 ` Andy Wingo @ 2016-06-12 8:25 ` Chris Vine 2016-06-19 9:13 ` Andy Wingo 0 siblings, 1 reply; 43+ messages in thread From: Chris Vine @ 2016-06-12 8:25 UTC (permalink / raw) To: Andy Wingo; +Cc: Ludovic Courtès, guile-user, guile-devel On Sat, 11 Jun 2016 19:02:09 +0200 Andy Wingo <wingo@pobox.com> wrote: > On Thu 14 Apr 2016 16:08, ludo@gnu.org (Ludovic Courtès) writes: > > > Andy Wingo <wingo@pobox.com> skribis: > > > >> I am working on improving our port implementation to take > >> advantage of the opportunity to break ABI in 2.2. I am wondering > >> how much I can break C API as well -- there are some changes that > >> would allow better user-space threading > >> (e.g. > >> http://thread.gmane.org/gmane.lisp.guile.devel/14158/focus=15463 > >> or Chris Webber's 8sync). But those changes would require some > >> incompatible changes to the C API related to port internals. This > >> API is pretty much only used when implementing port types. So, I > >> would like to collect a list of people that implement their own > >> ports :) I know Guile-GNOME does it for GNOME-VFS, though > >> GNOME-VFS is super-old at this point... Anyway. Looking forward > >> to your links :) > > > > What do you conclude from this poll? :-) > > > > From what you’ve seen, how do you think current uses would impact > > the refactoring work (and vice versa)? > > Sorry for the late response :) > > My conclusion is that we should not change anything about the Scheme > interface, but that with close communication with C port hackers, we > can feel OK about changing the C interface to make it both more > simple and more expressive. Since libguile is parallel-installable > and you have to select the version of your Guile when you build your > project, of course people will be able to update / upgrade when they > choose to. > > I put in a lot of effort to the documentation; check it out: > > http://www.gnu.org/software/guile/docs/master/guile.html/Input-and-Output.html The documentation indicates that with the C ports implementation in guile-2.2, reads will block on non-blocking file descriptors. This will stop the approach to asynchronicity used in 8sync and guile-a-sync (the latter of which I have written) from working correctly with sockets on linux operating systems, because at present both of these use guile's wrapper for select. This arises because with linux you can get spurious wake-ups with select() or poll() on sockets, whereby a read on a file descriptor for a socket can block even though the descriptor is reported by select() or poll() as ready for reading[1]. Presumably suspendable ports will use some method to work around unwanted blocking arising from spurious select()/poll() wake-ups. However, to cater for other asynchronous implementations of file watches, would it be possible to provide a configurable option either to retain the guile-2.0 behaviour in such cases (which is to throw a system-error with errno set to EAGAIN or EWOULDBLOCK), or to provide a non-blocking alternative whereby the read operation would, instead of blocking, return some special value such as an EAGAIN symbol? Either would enable user code then to resume to its prompt and let other code execute. Chris [1] The man page for select states "Under Linux, select() may report a socket file descriptor as 'ready for reading', while nevertheless a subsequent read blocks. This could for example happen when data has arrived but upon examination has wrong checksum and is discarded. There may be other circumstances in which a file descriptor is spuriously reported as ready. Thus it may be safer to use O_NONBLOCK on sockets that should not block." ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-12 8:25 ` Chris Vine @ 2016-06-19 9:13 ` Andy Wingo 2016-06-19 9:55 ` Marko Rauhamaa 2016-06-19 15:33 ` Chris Vine 0 siblings, 2 replies; 43+ messages in thread From: Andy Wingo @ 2016-06-19 9:13 UTC (permalink / raw) To: Chris Vine; +Cc: Ludovic Courtès, guile-user, guile-devel Hi :) On Sun 12 Jun 2016 10:25, Chris Vine <chris@cvine.freeserve.co.uk> writes: >> http://www.gnu.org/software/guile/docs/master/guile.html/Input-and-Output.html > > The documentation indicates that with the C ports implementation in > guile-2.2, reads will block on non-blocking file descriptors. Correct. > This will stop the approach to asynchronicity used in 8sync and > guile-a-sync (the latter of which I have written) from working > correctly with sockets on linux operating systems, because at present > both of these use guile's wrapper for select. The trouble is that AFAIU there is no way to make non-blocking input work reliably with O_NONBLOCK file descriptors in the approach that Guile has always used. As you know, the current behavior for Guile 2.0 is to throw an exception when you get EAGAIN / EWOULDBLOCK. If I am understanding you correctly, your approach is to only read from a port if you have done a select() / poll() / etc on it beforehand indicating that you can read at least one byte. The problem with this is not only spurious wakeups, as you note, but also buffering. Throwing an exception when reading in Guile 2.0 will discard input buffers in many cases. Likewise when writing, you won't be able to know how much you've written. This goes not only for the explicit bufffers attached to ports and which you can control with `setvbuf', but also implicit buffers, and it's in this case that it's particularly pernicious: if you `read-char' on a UTF-8 port, you might end up using local variables in the stack as a buffer for reconstructing that codepoint. If you throw an exception in the middle, you discard those bytes. Likewise for writing. For suspendable ports, you don't throw an exception: you just assume the operation is going to work, but if you get EAGAIN / EWOULDBLOCK, you call the current-read-waiter / current-write-waiter and when that returns retry the operation. Since it operates on the lowest level of bytes, it's reliable. Looping handles the spurious wakeup case. > However, to cater for other asynchronous implementations of file > watches, would it be possible to provide a configurable option either > to retain the guile-2.0 behaviour in such cases (which is to throw a > system-error with errno set to EAGAIN or EWOULDBLOCK), or to provide a > non-blocking alternative whereby the read operation would, instead of > blocking, return some special value such as an EAGAIN symbol? Either > would enable user code then to resume to its prompt and let other code > execute. Why not just (install-suspendable-ports!) and (parameterize ((current-read-waiter my-read-waiter)) ...) etc? It is entirely possible with Guile 2.1.3 to build an asynchronous coroutine-style concurrent system in user-space using these primitives. See the wip-ethread branch for an example implementation. Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-19 9:13 ` Andy Wingo @ 2016-06-19 9:55 ` Marko Rauhamaa 2016-06-19 15:27 ` Andy Wingo 2016-06-19 15:33 ` Chris Vine 1 sibling, 1 reply; 43+ messages in thread From: Marko Rauhamaa @ 2016-06-19 9:55 UTC (permalink / raw) To: Andy Wingo; +Cc: Ludovic Courtès, guile-devel, guile-user Andy Wingo <wingo@pobox.com>: > The trouble is that AFAIU there is no way to make non-blocking input > work reliably with O_NONBLOCK file descriptors in the approach that > Guile has always used. > > [...] > > The problem with this is not only spurious wakeups, as you note, but > also buffering. Throwing an exception when reading in Guile 2.0 will > discard input buffers in many cases. Likewise when writing, you won't > be able to know how much you've written. Guile's POSIX support provides immediate access to most OS facilities. However, support for read(2) and write(2) seem to be missing. (Note that Python, for example, provides both buffered, native facilities and the low-level os.read() and os.write().) Sure, recv! and send are there, but asynchronous I/O is more general than socket communication. > For suspendable ports, you don't throw an exception: you just assume > the operation is going to work, but if you get EAGAIN / EWOULDBLOCK, > you call the current-read-waiter / current-write-waiter and when that > returns retry the operation. Since it operates on the lowest level of > bytes, it's reliable. Looping handles the spurious wakeup case. The POSIX system call simply returns whatever bytes are available at the moment. That paradigm works perfectly. IOW, if you have buffered 10 bytes and get an EGAIN, just return those 10 bytes. > Why not just (install-suspendable-ports!) and > > (parameterize ((current-read-waiter my-read-waiter)) ...) > > etc? It is entirely possible with Guile 2.1.3 to build an asynchronous > coroutine-style concurrent system in user-space using these > primitives. See the wip-ethread branch for an example implementation. I haven't thought it through if that callback paradigm would offer the same degrees of freedom as the return-whatever-you-have paradigm. It seems unnecessarily complicated, though. I would simply violate the letter of the R?RS port semantics in Guile and go with the POSIX semantics. It seems clear the R?RS port specification was written without considering asynchronous communication at all. Marko ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-19 9:55 ` Marko Rauhamaa @ 2016-06-19 15:27 ` Andy Wingo 0 siblings, 0 replies; 43+ messages in thread From: Andy Wingo @ 2016-06-19 15:27 UTC (permalink / raw) To: Marko Rauhamaa; +Cc: Ludovic Courtès, guile-devel, guile-user Hi, You are arguing for new I/O primitives with different semantics, and that's fine and good :) My goal was to add the ability to sensibly work with non-blocking ports using Guile's existing primitives, especially the textual ones. This effort was started because of the ethreads work I did about four years ago. That required a separate implementation of buffered ports, which is fine for a library but not good for a system. You want to be able to set a non-blocking port as the current input port, for example, and you want to be able to (display x p) where p is a non-blocking port, and you want to be able to do this both in a blocking way, because Guile doesn't currently include any kind of user-space scheduler, but also you want to support experiments to make a user-space scheduler. The ethreads work from four years ago also required me to patch various parts of Guile, like the web server, so that they could use different primitives. That's what I want to avoid. I want to be able to run an SSAX parser or a Scheme reader or writer over a non-blocking port and have it just work. (For SSAX it's close to working; there are a couple of primitives that need to be reimplemented in terms of the subset of port interfaces that suspendable ports replaces. For Scheme `read'/`write' the situation is more complicated.) On Sun 19 Jun 2016 11:55, Marko Rauhamaa <marko@pacujo.net> writes: > Guile's POSIX support provides immediate access to most OS facilities. > However, support for read(2) and write(2) seem to be missing. (Note that > Python, for example, provides both buffered, native facilities and the > low-level os.read() and os.write().) A read operation is supported via `get-bytevector-some', but it's true that `put-bytevector-some' is not implemented. Incidentally, have you seen ljsyscall, for LuaJIT? What a great library. I wish we had something like that for Guile. > I haven't thought it through if that callback paradigm would offer the > same degrees of freedom as the return-whatever-you-have paradigm. It > seems unnecessarily complicated, though. There is complication, but it is necessary complication. If you want to support blocking textual operations with all of the encodings that Guile supports over non-blocking ports, you need to be able to poll() when you get EAGAIN; and if you also want to be able to suspend in the middle of writing a byte sequence, many levels removed from the original write, then you need a parameter; and if you don't know yet the entire set of things you might want to do, that parameter better be a procedure. In practice I find it a delight to work with this interface. See the memcached server and client implementations in the recently updated wip-ethreads branch; to my eye they are lovely. Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-19 9:13 ` Andy Wingo 2016-06-19 9:55 ` Marko Rauhamaa @ 2016-06-19 15:33 ` Chris Vine 2016-06-19 17:48 ` Andy Wingo 1 sibling, 1 reply; 43+ messages in thread From: Chris Vine @ 2016-06-19 15:33 UTC (permalink / raw) To: guile-user, guile-devel On Sun, 19 Jun 2016 11:13:17 +0200 Andy Wingo <wingo@pobox.com> wrote: > Hi :) > > On Sun 12 Jun 2016 10:25, Chris Vine <chris@cvine.freeserve.co.uk> > writes: > > >> http://www.gnu.org/software/guile/docs/master/guile.html/Input-and-Output.html > > > > The documentation indicates that with the C ports implementation in > > guile-2.2, reads will block on non-blocking file descriptors. > > Correct. > > > This will stop the approach to asynchronicity used in 8sync and > > guile-a-sync (the latter of which I have written) from working > > correctly with sockets on linux operating systems, because at > > present both of these use guile's wrapper for select. > > The trouble is that AFAIU there is no way to make non-blocking input > work reliably with O_NONBLOCK file descriptors in the approach that > Guile has always used. > > As you know, the current behavior for Guile 2.0 is to throw an > exception when you get EAGAIN / EWOULDBLOCK. If I am understanding > you correctly, your approach is to only read from a port if you have > done a select() / poll() / etc on it beforehand indicating that you > can read at least one byte. My approach would be to to do that, if it worked. And it does work with pipes and unix domain sockets provided you only read a byte at a time (see further below), but does not work on linux with TCP sockets because linux's select() and poll() system calls are not POSIX compliant. Therefore with TCP sockets on linux you have to use non-blocking reads and cater for the possibility of an EAGAIN/EWOULDBLOCK exception. > The problem with this is not only spurious wakeups, as you note, but > also buffering. Throwing an exception when reading in Guile 2.0 will > discard input buffers in many cases. Likewise when writing, you won't > be able to know how much you've written. > > This goes not only for the explicit bufffers attached to ports and > which you can control with `setvbuf', but also implicit buffers, and > it's in this case that it's particularly pernicious: if you > `read-char' on a UTF-8 port, you might end up using local variables > in the stack as a buffer for reconstructing that codepoint. If you > throw an exception in the middle, you discard those bytes. Likewise > for writing. I recognise this problem. The answer I have adopted when reading from TCP sockets is to extract individual bytes only from the port into a bytevector using R6RS's get-u8 procedure and (if the port is textual rather than binary) to reconstruct characters from that using bytevector->string at, say, a line end[1]. An EAGAIN/EWOULDBLOCK exception is then just treated as an invitation to return to the prompt, and read state is retained in the bytevector. In effect this is doing by hand what a more complete non-blocking EAGAIN-safe port implementation might otherwise do for you. Writing is something else. To do it effectively the writer to the port must in any event cater for the fact that when the buffer is full but the underlying file descriptor is ready for writing, the next write will cause a buffer flush, and if the size of the buffer is greater than the number of characters that the file can receive without blocking, blocking might still occur. You usually need to switch off buffering for writes (but you quite often may want to do that anyway on output ports for sockets). > For suspendable ports, you don't throw an exception: you just assume > the operation is going to work, but if you get EAGAIN / EWOULDBLOCK, > you call the current-read-waiter / current-write-waiter and when that > returns retry the operation. Since it operates on the lowest level of > bytes, it's reliable. Looping handles the spurious wakeup case. > > > However, to cater for other asynchronous implementations of file > > watches, would it be possible to provide a configurable option > > either to retain the guile-2.0 behaviour in such cases (which is to > > throw a system-error with errno set to EAGAIN or EWOULDBLOCK), or > > to provide a non-blocking alternative whereby the read operation > > would, instead of blocking, return some special value such as an > > EAGAIN symbol? Either would enable user code then to resume to its > > prompt and let other code execute. > > Why not just (install-suspendable-ports!) and > > (parameterize ((current-read-waiter my-read-waiter)) ...) > > etc? It is entirely possible with Guile 2.1.3 to build an > asynchronous coroutine-style concurrent system in user-space using > these primitives. See the wip-ethread branch for an example > implementation. I would want to continue using an external event loop implemented with poll() or select() and delimited continuations. This makes it relatively trivial to implement asynchronous timeouts and single-threaded task multiplexing (albeit co-operative rather than pre-emptive) as well as file operations, and would also enable the glib event loop to be used for programs which happen to use guile-gnome (although guile-gnome has other issues at present). I don't think I have got to grips with how to do that with read-waiter, because the read-waiter comprises in effect another loop (in which the main event loop with its own prompts would have to run) until the read request has been satisfied. I would need to think about it. Since ethreads use a poll()/epoll() loop, presumably you think it is straightforward enough to integrate the two, even if at present I don't. Writing custom C ports which do what I want with non-blocking descriptors is another option, but I would hope that could be avoided: at that point one would probably use some other solution entirely. On a side issue, I am still trying to understand the point of causing guile-2.2's read of a non-blocking C port to block. The whole point of making a descriptor non-blocking is that that shouldn't happen, and there are circumstances where pealing individual bytes off a non-blocking port as they become available is what you want to do. It makes guile's select wrapper unusable with TCP sockets on linux. I understand that suspendable-ports work differently, but that is another matter. Chris [1] A sample implementation can be seen at https://github.com/ChrisVine/guile-a-sync/blob/master/a-sync/event-loop.scm from line 900 on, and elsewhere in that file. ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-19 15:33 ` Chris Vine @ 2016-06-19 17:48 ` Andy Wingo 2016-06-19 20:09 ` Chris Vine 0 siblings, 1 reply; 43+ messages in thread From: Andy Wingo @ 2016-06-19 17:48 UTC (permalink / raw) To: Chris Vine; +Cc: guile-user, guile-devel On Sun 19 Jun 2016 17:33, Chris Vine <chris@cvine.freeserve.co.uk> writes: > The answer I have adopted when reading from TCP sockets is to extract > individual bytes only from the port into a bytevector using R6RS's > get-u8 procedure and (if the port is textual rather than binary) to > reconstruct characters from that using bytevector->string at, say, a > line end[1]. An EAGAIN/EWOULDBLOCK exception is then just treated as > an invitation to return to the prompt, and read state is retained in > the bytevector. Yep, makes sense, though it will be really slow of course. And, you'd have to make people rewrite their code to use your I/O primitives instead of using read-line from (ice-9 rdelim); a bit of a drag, but OK. >> Why not just (install-suspendable-ports!) and >> >> (parameterize ((current-read-waiter my-read-waiter)) ...) >> >> etc? It is entirely possible with Guile 2.1.3 to build an >> asynchronous coroutine-style concurrent system in user-space using >> these primitives. See the wip-ethread branch for an example >> implementation. > > I would want to continue using an external event loop implemented with > poll() or select() and delimited continuations. Yes yes a million times yes! That's what I want too. To that end, the current-read-waiter parameter's value is a function which takes the port being waited on, and when it returns the operation will retry. In wip-ethreads I suspend to a prompt, add the port's fd to an epoll set, and when the fd becomes readable again we resume the continuation, resulting in a return from the current-read-waiter. > I don't think I have got to grips with how to do that with read-waiter, > because the read-waiter comprises in effect another loop (in which the > main event loop with its own prompts would have to run) until the read > request has been satisfied. I would need to think about it. Since > ethreads use a poll()/epoll() loop, presumably you think it is > straightforward enough to integrate the two, even if at present I don't. Here is the code. First, a helper: ;; The AFTER-SUSPEND thunk allows the user to suspend the current ;; thread, saving its state, and then perform some other nonlocal ;; control flow. ;; (define* (suspend #:optional (after-suspend (lambda (ctx thread) #f))) ((abort-to-prompt (econtext-prompt-tag (current-econtext)) after-suspend))) Assume there is some current-econtext parameter with a "context" which holds the prompt tag associated with the scheduler. As you see when the continuation resumes, it resumes by calling a thunk, allowing exceptions to be thrown from the context of the suspend. Here's the main loop function, which you could replace with the GLib main loop or whatever: (define* (run #:optional (ctx (ensure-current-econtext)) #:key (install-suspendable-ports? #t)) (when install-suspendable-ports? (install-suspendable-ports!)) (parameterize ((current-econtext ctx) (current-read-waiter wait-for-readable) (current-write-waiter wait-for-writable)) (let lp () (run-ethread ctx (next-thread ctx)) (lp)))) Cool. Now the wait functions: (define (wait-for-readable port) (wait-for-events port (port-read-wait-fd port) (logior EPOLLIN EPOLLRDHUP))) (define (wait-for-writable port) (wait-for-events port (port-write-wait-fd port) EPOLLOUT)) Now the wait-for-events function: (define (wait-for-events port fd events) (handle-events port events (suspend (lambda (ctx thread) ...)))) Well that's a mess, but the thing to know is that the `suspend' will abort to the relevant prompt, and then invoke the thunk that's its argument. Here's `handle-events' and we are done: (define (handle-events port events revents) (unless (zero? (logand revents EPOLLERR)) (error "error reading from port" port))) But I guess that error could be "reading or writing"; oh well. See http://git.savannah.gnu.org/cgit/guile.git/tree/module/ice-9/ethreads.scm?h=wip-ethreads&id=253dc1a7114b89351a3aa330caf173b98c5a65dd, it's not long but it takes some time to read. I think I can fairly ask that of you though, given your interest in this area :) > On a side issue, I am still trying to understand the point of causing > guile-2.2's read of a non-blocking C port to block. The whole point of > making a descriptor non-blocking is that that shouldn't happen, and > there are circumstances where pealing individual bytes off a > non-blocking port as they become available is what you want to do. It > makes guile's select wrapper unusable with TCP sockets on linux. I > understand that suspendable-ports work differently, but that is another > matter. I would start by saying that many things are possible including adding the ability to enable the error-throwing behavior. I think your use case is a great one and we should support it well. I think though that perhaps you didn't see how the waiter parameters could apply to your use case; are things clearer now? I think that once you allow yourself to use port buffers, things will be much easier and much more efficient as well. But, I could be misunderstanding too. Guile's I/O in the past was very oriented towards ASCII. 2.0 went a long way towards good textual I/O with other encodings, and with 2.2 we will have good textual non-blocking I/O. It could be that by focusing on this, I have neglected some binary non-blocking I/O use cases, though I think that get-bytevector-some, having enabled suspendable ports, serves many of these purposes very well. But if there is a need for an additional binary I/O primitive, let's figure out what it is and implement it. Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-19 17:48 ` Andy Wingo @ 2016-06-19 20:09 ` Chris Vine 2016-06-20 3:38 ` William ML Leslie 0 siblings, 1 reply; 43+ messages in thread From: Chris Vine @ 2016-06-19 20:09 UTC (permalink / raw) To: guile-user, guile-devel On Sun, 19 Jun 2016 19:48:03 +0200 Andy Wingo <wingo@pobox.com> wrote: > On Sun 19 Jun 2016 17:33, Chris Vine <chris@cvine.freeserve.co.uk> > writes: > > > The answer I have adopted when reading from TCP sockets is to > > extract individual bytes only from the port into a bytevector using > > R6RS's get-u8 procedure and (if the port is textual rather than > > binary) to reconstruct characters from that using > > bytevector->string at, say, a line end[1]. An EAGAIN/EWOULDBLOCK > > exception is then just treated as an invitation to return to the > > prompt, and read state is retained in the bytevector. > > Yep, makes sense, though it will be really slow of course. And, you'd > have to make people rewrite their code to use your I/O primitives > instead of using read-line from (ice-9 rdelim); a bit of a drag, but > OK. It's not excessively slow provided that the port itself (as opposed to the conversion to unicode characters, which you have to do yourself) can be buffered. You can loop calls to get-u8 on char-ready? to vacate the buffers so far as wanted, without returning to the prompt for every byte. From that point of view it shouldn't be that much slower than repeatedly calling read-char on a port with latin-1 encoding on a normal blocking port. [snip] > > I don't think I have got to grips with how to do that with > > read-waiter, because the read-waiter comprises in effect another > > loop (in which the main event loop with its own prompts would have > > to run) until the read request has been satisfied. I would need to > > think about it. Since ethreads use a poll()/epoll() loop, > > presumably you think it is straightforward enough to integrate the > > two, even if at present I don't. > > Here is the code. First, a helper: > > ;; The AFTER-SUSPEND thunk allows the user to suspend the current > ;; thread, saving its state, and then perform some other nonlocal > ;; control flow. > ;; > (define* (suspend #:optional (after-suspend (lambda (ctx thread) > #f))) ((abort-to-prompt (econtext-prompt-tag (current-econtext)) > after-suspend))) > > Assume there is some current-econtext parameter with a "context" which > holds the prompt tag associated with the scheduler. As you see when > the continuation resumes, it resumes by calling a thunk, allowing > exceptions to be thrown from the context of the suspend. > > Here's the main loop function, which you could replace with the GLib > main loop or whatever: > > (define* (run #:optional (ctx (ensure-current-econtext)) > #:key (install-suspendable-ports? #t)) > (when install-suspendable-ports? (install-suspendable-ports!)) > (parameterize ((current-econtext ctx) > (current-read-waiter wait-for-readable) > (current-write-waiter wait-for-writable)) > (let lp () > (run-ethread ctx (next-thread ctx)) > (lp)))) > > Cool. Now the wait functions: > > (define (wait-for-readable port) > (wait-for-events port (port-read-wait-fd port) (logior EPOLLIN > EPOLLRDHUP))) > > (define (wait-for-writable port) > (wait-for-events port (port-write-wait-fd port) EPOLLOUT)) > > Now the wait-for-events function: > > (define (wait-for-events port fd events) > (handle-events > port > events > (suspend > (lambda (ctx thread) > ...)))) > > Well that's a mess, but the thing to know is that the `suspend' will > abort to the relevant prompt, and then invoke the thunk that's its > argument. Here's `handle-events' and we are done: > > (define (handle-events port events revents) > (unless (zero? (logand revents EPOLLERR)) > (error "error reading from port" port))) > > But I guess that error could be "reading or writing"; oh well. > > See > http://git.savannah.gnu.org/cgit/guile.git/tree/module/ice-9/ethreads.scm?h=wip-ethreads&id=253dc1a7114b89351a3aa330caf173b98c5a65dd, > it's not long but it takes some time to read. I think I can fairly > ask that of you though, given your interest in this area :) OK I am grateful for your patience in explaining this. I need to think about it, but while this works where all events come from user-derived events, I doubt that this would work with guile-gnome and the glib main loop in the round, because for gtk+ to function the glib main loop must block within glib's own internal call to the poll() and not within the guile prompt, or gdk and other events will jam up. You would probably need to run the glib main loop in its own (pthread) thread, about which you have previously given dire warnings so far as guile as it currently stands is concerned. As I say, I really need to think more about this and look further at the code. I may well be missing something. > > On a side issue, I am still trying to understand the point of > > causing guile-2.2's read of a non-blocking C port to block. The > > whole point of making a descriptor non-blocking is that that > > shouldn't happen, and there are circumstances where pealing > > individual bytes off a non-blocking port as they become available > > is what you want to do. It makes guile's select wrapper unusable > > with TCP sockets on linux. I understand that suspendable-ports > > work differently, but that is another matter. > > I would start by saying that many things are possible including adding > the ability to enable the error-throwing behavior. I think your use > case is a great one and we should support it well. I think though > that perhaps you didn't see how the waiter parameters could apply to > your use case; are things clearer now? I think that once you allow > yourself to use port buffers, things will be much easier and much > more efficient as well. But, I could be misunderstanding too. > > Guile's I/O in the past was very oriented towards ASCII. 2.0 went a > long way towards good textual I/O with other encodings, and with 2.2 > we will have good textual non-blocking I/O. It could be that by > focusing on this, I have neglected some binary non-blocking I/O use > cases, though I think that get-bytevector-some, having enabled > suspendable ports, serves many of these purposes very well. But if > there is a need for an additional binary I/O primitive, let's figure > out what it is and implement it. This is an issue with R6RS. In my view it ought to have a get-bytevector-some! procedure which emulates unix read(), but once you decide that C ports should not have non-blocking reads, that is an end to it really. In my view though, a non-blocking get-bytevector-some! would be really nice. It could return the number of bytes extracted, with a return value of 0 for EAGAIN. Chris ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-19 20:09 ` Chris Vine @ 2016-06-20 3:38 ` William ML Leslie 2016-06-20 6:45 ` Chris Vine 0 siblings, 1 reply; 43+ messages in thread From: William ML Leslie @ 2016-06-20 3:38 UTC (permalink / raw) To: Chris Vine; +Cc: guile-user, guile-devel On 20 June 2016 at 06:09, Chris Vine <chris@cvine.freeserve.co.uk> wrote: > OK I am grateful for your patience in explaining this. I need to think > about it, but while this works where all events come from user-derived > events, I doubt that this would work with guile-gnome and the glib main > loop in the round, because for gtk+ to function the glib main loop must > block within glib's own internal call to the poll() and not within the > guile prompt, or gdk and other events will jam up. You would probably > need to run the glib main loop in its own (pthread) thread, about which > you have previously given dire warnings so far as guile as it currently > stands is concerned. > > As I say, I really need to think more about this and look further at > the code. I may well be missing something. If the event loop is external as you suggested, you could use a different implementation of the loop that used g_io_add_watch and returned to the glib mainloop when you're in a glib or gtk application. -- William Leslie Notice: Likely much of this email is, by the nature of copyright, covered under copyright law. You absolutely MAY reproduce any part of it in accordance with the copyright law of the nation you are reading this in. Any attempt to DENY YOU THOSE RIGHTS would be illegal without prior contractual agreement. ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-20 3:38 ` William ML Leslie @ 2016-06-20 6:45 ` Chris Vine 2016-06-20 7:34 ` Andy Wingo 0 siblings, 1 reply; 43+ messages in thread From: Chris Vine @ 2016-06-20 6:45 UTC (permalink / raw) To: guile-user, guile-devel On Mon, 20 Jun 2016 13:38:39 +1000 William ML Leslie <william.leslie.ttg@gmail.com> wrote: > On 20 June 2016 at 06:09, Chris Vine <chris@cvine.freeserve.co.uk> > wrote: > > OK I am grateful for your patience in explaining this. I need to > > think about it, but while this works where all events come from > > user-derived events, I doubt that this would work with guile-gnome > > and the glib main loop in the round, because for gtk+ to function > > the glib main loop must block within glib's own internal call to > > the poll() and not within the guile prompt, or gdk and other events > > will jam up. You would probably need to run the glib main loop in > > its own (pthread) thread, about which you have previously given > > dire warnings so far as guile as it currently stands is concerned. > > > > As I say, I really need to think more about this and look further at > > the code. I may well be missing something. > > If the event loop is external as you suggested, you could use a > different implementation of the loop that used g_io_add_watch and > returned to the glib mainloop when you're in a glib or gtk > application. Sure, but that's not really the difficulty. On further reflection on Andy's post, I think what I understand to be his approach to suspendable-ports could be made to work with a glib main loop without invoking additional pthread threads, but I think it would be barely scalable or tractable. For Andy's sake, can I give a concrete example of an entirely single threaded program? Let's say the program uses guile-gnome and is running a glib main loop, on which the program is (of necessity if using a glib main loop) blocking in g-main-loop-run, so all code is running as events in the main loop. For simplicity, let's say you have a file watch in the glib event loop which has made a non-blocking read of the first byte of a multi-byte UTF-8 character, and the suspendable-ports implementation is in use because it is a non-blocking read of a TCP socket. (It could be something different: there may be a non-blocking read request for a complete line of text which has so far only partially been satisfied, but the partially complete character is the easiest example to deal with.) The read request is therefore in read-waiter waiting for a complete UTF-8 byte sequence to arrive. On the current hypothesis, read-waiter comprises a procedure which is suspended on a prompt waiting for an event to occur in the glib main loop which will cause it to resume, comprising the file descriptor becoming ready which will satisfy the read request. But while suspended in read-waiter, this prompt would have to service any user event sources which might become ready in the glib main loop, not just the particular file descriptor in question becoming ready. These could be nested with other non-blocking reads, also in read-waiter, on other descriptors which also have a pending partial read, or with time-outs or with tasks composed with idle handlers. Any user event source which has been attached to the glib main loop which fires would have to cause all pending partial reads to go through the nested non-blocking read cycles to see if anything is available, as well as servicing the particular user event in question. It seems hardly scalable, if it would work at all. My approach on the other hand does not nest events from the glib main loop in this way. It is still possible I have missed something. The proof of all these things is in the pudding. I suppose what is needed is an attempt at a practical implementation using read-waiter: but for the moment I don't see it. Chris ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-20 6:45 ` Chris Vine @ 2016-06-20 7:34 ` Andy Wingo 2016-06-20 9:01 ` Chris Vine 0 siblings, 1 reply; 43+ messages in thread From: Andy Wingo @ 2016-06-20 7:34 UTC (permalink / raw) To: Chris Vine; +Cc: guile-user, guile-devel On Mon 20 Jun 2016 08:45, Chris Vine <chris@cvine.freeserve.co.uk> writes: > For simplicity, let's say you have a file watch in the glib event loop > which has made a non-blocking read of the first byte of a multi-byte > UTF-8 character, and the suspendable-ports implementation is in use > because it is a non-blocking read of a TCP socket. (It could be > something different: there may be a non-blocking read request for a > complete line of text which has so far only partially been satisfied, > but the partially complete character is the easiest example to deal > with.) The read request is therefore in read-waiter waiting for a > complete UTF-8 byte sequence to arrive. On the current hypothesis, > read-waiter comprises a procedure which is suspended on a prompt > waiting for an event to occur in the glib main loop which will cause > it to resume, comprising the file descriptor becoming ready which will > satisfy the read request. But while suspended in read-waiter, this > prompt would have to service any user event sources which might become > ready in the glib main loop, not just the particular file descriptor > in question becoming ready. I must not be communicating clearly because this is definitely not what I am proposing. The prompt doesn't service anything, and it's just the one user-space thread which is suspended, and when it suspends, it suspends back to the main loop which runs as usual, timers and all. prompt /------\ /------------\|/-----------\ /------------\ /----------\ | main --> run-thread -|>(user code)--> read-char --> waiter | | loop | | ||| | | | | | \------/ \------------/|\-----------/ \------------/ \--|-------/ ^ | \-------------------------------/ stack grows this way -> The current-read-waiter aborts to a prompt. That prompt is instated when the thread is run or resumed. When you abort to that prompt, you add the FD to the poll set / main loop / *, remember the delimited continuation, and return to the main loop. When the fd becomes readable or the gsource fires or whatever, you reinstate the delimited continuation via a new invocation of run-thread (prompt and all). > My approach on the other hand does not nest events from the glib main > loop in this way. "Nesting events" should not be a thing you have to do. Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-20 7:34 ` Andy Wingo @ 2016-06-20 9:01 ` Chris Vine 2016-06-22 22:44 ` Chris Vine 0 siblings, 1 reply; 43+ messages in thread From: Chris Vine @ 2016-06-20 9:01 UTC (permalink / raw) To: guile-user, guile-devel On Mon, 20 Jun 2016 09:34:26 +0200 Andy Wingo <wingo@pobox.com> wrote: [snip] > I must not be communicating clearly because this is definitely not > what I am proposing. The prompt doesn't service anything, and it's > just the one user-space thread which is suspended, and when it > suspends, it suspends back to the main loop which runs as usual, > timers and all. > > prompt > /------\ /------------\|/-----------\ /------------\ /----------\ > | main --> run-thread -|>(user code)--> read-char --> waiter | > | loop | | ||| | | | | | > \------/ \------------/|\-----------/ \------------/ \--|-------/ > ^ | > \-------------------------------/ > stack grows this way -> > > The current-read-waiter aborts to a prompt. That prompt is instated > when the thread is run or resumed. When you abort to that prompt, you > add the FD to the poll set / main loop / *, remember the delimited > continuation, and return to the main loop. When the fd becomes > readable or the gsource fires or whatever, you reinstate the > delimited continuation via a new invocation of run-thread (prompt and > all). Ah right, that is clearer, thank you. There would indeed be a prompt for each user glib event source comprised in the "thread" abstraction, which the read-waiter (or whatever) aborts to. It is that abstraction that I was missing and will need to look at. Chris ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-20 9:01 ` Chris Vine @ 2016-06-22 22:44 ` Chris Vine 2016-06-23 7:36 ` Andy Wingo 0 siblings, 1 reply; 43+ messages in thread From: Chris Vine @ 2016-06-22 22:44 UTC (permalink / raw) To: guile-user, guile-devel On Mon, 20 Jun 2016 10:01:57 +0100 Chris Vine <chris@cvine.freeserve.co.uk> wrote: > On Mon, 20 Jun 2016 09:34:26 +0200 > Andy Wingo <wingo@pobox.com> wrote: > [snip] > > I must not be communicating clearly because this is definitely not > > what I am proposing. The prompt doesn't service anything, and it's > > just the one user-space thread which is suspended, and when it > > suspends, it suspends back to the main loop which runs as usual, > > timers and all. > > > > prompt > > /------\ /------------\|/-----------\ /------------\ /----------\ > > | main --> run-thread -|>(user code)--> read-char --> waiter | > > | loop | | ||| | | | | | > > \------/ \------------/|\-----------/ \------------/ \--|-------/ > > ^ | > > \-------------------------------/ > > stack grows this way -> > > > > The current-read-waiter aborts to a prompt. That prompt is instated > > when the thread is run or resumed. When you abort to that prompt, > > you add the FD to the poll set / main loop / *, remember the > > delimited continuation, and return to the main loop. When the fd > > becomes readable or the gsource fires or whatever, you reinstate the > > delimited continuation via a new invocation of run-thread (prompt > > and all). > > Ah right, that is clearer, thank you. There would indeed be a prompt > for each user glib event source comprised in the "thread" abstraction, > which the read-waiter (or whatever) aborts to. It is that abstraction > that I was missing and will need to look at. I have stirred myself and installed guile-2.1.3. On looking more at the suspendable ports code it became obvious and I haven't needed to adopt anything like ethreads with its "thread" abstraction: instead I have kept the approach already adopted in the guile-a-sync library. However, the consequence of using suspendable ports instead of C ports is that the await-getline! procedure (as an example) has been reduced to a mere 16 lines of code, mainly because it is possible to use (ice-9 rdelim)'s read-line procedure with non-blocking ports. I have made a new repository for guile-a-sync for use with guile-2.2 and when I am happy with the new interfaces (and assuming nothing else goes wrong) I will put it up on github. This is very nice. Thanks for taking the time to go through it with me. Chris ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-22 22:44 ` Chris Vine @ 2016-06-23 7:36 ` Andy Wingo 2016-06-23 8:56 ` Andy Wingo 2016-06-23 9:24 ` Chris Vine 0 siblings, 2 replies; 43+ messages in thread From: Andy Wingo @ 2016-06-23 7:36 UTC (permalink / raw) To: Chris Vine; +Cc: guile-user, guile-devel On Thu 23 Jun 2016 00:44, Chris Vine <chris@cvine.freeserve.co.uk> writes: > I have stirred myself and installed guile-2.1.3. On looking more at > the suspendable ports code it became obvious and I haven't needed to > adopt anything like ethreads with its "thread" abstraction: instead I > have kept the approach already adopted in the guile-a-sync library. Excellent. Though I think that eventually we will want to bless one of these concurrency patterns as the default one, we're a long way away from that, and even if we do bless one I think we will always want to allow people to experiment and deploy different ones. So, great, glad to hear you are doing work in this area :) > However, the consequence of using suspendable ports instead of C ports > is that the await-getline! procedure (as an example) has been reduced > to a mere 16 lines of code, mainly because it is possible to use (ice-9 > rdelim)'s read-line procedure with non-blocking ports. Excellent! That's the result I was hoping for. > I have made a new repository for guile-a-sync for use with guile-2.2 and > when I am happy with the new interfaces (and assuming nothing else goes > wrong) I will put it up on github. > > This is very nice. Thanks for taking the time to go through it with me. Thank you for your patience too! The only drawback that I know of with the strategy of simply allowing users to use Guile's I/O primitives (e.g., `read-line') and assuming that they'll suspend when they block is that not all of the primitives that you would like to you have been rebased on top of the ones implemented by (ice-9 suspendable-ports). So if you find such an I/O procedure that needs to be rewritten to use put-string instead of display, for example, then please do send a patch. As for `display' and `write'... I guess we can at least make suspendable ports handle these in a non-blocking way for some limited number of data types like strings, characters, and symbols, and dispatch to the C `display' implementation for more complicated data types like SMOBs or so. We'll see I guess. Happy hacking, Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-23 7:36 ` Andy Wingo @ 2016-06-23 8:56 ` Andy Wingo 2016-06-23 9:24 ` Chris Vine 1 sibling, 0 replies; 43+ messages in thread From: Andy Wingo @ 2016-06-23 8:56 UTC (permalink / raw) To: Chris Vine; +Cc: guile-user, guile-devel Little typo: On Thu 23 Jun 2016 09:36, Andy Wingo <wingo@pobox.com> writes: > The only drawback that I know of with the strategy of simply allowing > users to use Guile's I/O primitives (e.g., `read-line') and assuming > that they'll suspend when they block is that not all of the primitives > that you would like to you have been rebased on top of the ones s/you/use/ > implemented by (ice-9 suspendable-ports). So if you find such an I/O > procedure that needs to be rewritten to use put-string instead of > display, for example, then please do send a patch. Cheers, Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-23 7:36 ` Andy Wingo 2016-06-23 8:56 ` Andy Wingo @ 2016-06-23 9:24 ` Chris Vine 2016-06-23 9:50 ` Marko Rauhamaa 2016-06-23 10:43 ` Andy Wingo 1 sibling, 2 replies; 43+ messages in thread From: Chris Vine @ 2016-06-23 9:24 UTC (permalink / raw) To: guile-user, guile-devel On Thu, 23 Jun 2016 09:36:48 +0200 Andy Wingo <wingo@pobox.com> wrote: [snip] > Excellent. Though I think that eventually we will want to bless one > of these concurrency patterns as the default one, we're a long way > away from that, and even if we do bless one I think we will always > want to allow people to experiment and deploy different ones. So, > great, glad to hear you are doing work in this area :) A few things on that. First, there will always be a use for an event loop to do event-loopy things, irrespective of whether and how a coroutine interface is put around it. Sometimes you want to abstract things away, sometimes you don't. Secondly, as I understand it in the end you want pre-emptive "green" threads for guile, whereas my code equates to co-operative multi-tasking, whilst also working with native threads. I must come clean and say that I don't like "green" threads. Which leads on to the third point, which is that I would like to see guile match its words (in its documentation) with respect to native threads. I have found they work fine, with caution about shared global state. You think they don't, except possibly on intel, because some of its lock-free structures/variables -- and I think you are possibly referring to the VM here -- lack appropriate fences or other atomics. (The higher level C and scheme code has plainly had serious attempts at thread-safety made for it using mutexes.) Chris ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-23 9:24 ` Chris Vine @ 2016-06-23 9:50 ` Marko Rauhamaa 2016-06-23 10:43 ` Andy Wingo 1 sibling, 0 replies; 43+ messages in thread From: Marko Rauhamaa @ 2016-06-23 9:50 UTC (permalink / raw) To: Chris Vine; +Cc: guile-user, guile-devel Chris Vine <chris@cvine.freeserve.co.uk>: > First, there will always be a use for an event loop to do event-loopy > things, irrespective of whether and how a coroutine interface is put > around it. Sometimes you want to abstract things away, sometimes you > don't. Callback hell is my preferred programming paradigm. Any attempts to abstract it away are going to lead to a worse mess. Marko ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-23 9:24 ` Chris Vine 2016-06-23 9:50 ` Marko Rauhamaa @ 2016-06-23 10:43 ` Andy Wingo 2016-06-23 11:49 ` William ML Leslie 1 sibling, 1 reply; 43+ messages in thread From: Andy Wingo @ 2016-06-23 10:43 UTC (permalink / raw) To: Chris Vine; +Cc: guile-user, guile-devel On Thu 23 Jun 2016 11:24, Chris Vine <chris@cvine.freeserve.co.uk> writes: > A few things on that. First, there will always be a use for an event > loop to do event-loopy things, irrespective of whether and how a > coroutine interface is put around it. Sometimes you want to abstract > things away, sometimes you don't. Yep! > Secondly, as I understand it in the end you want pre-emptive "green" > threads for guile, whereas my code equates to co-operative > multi-tasking, whilst also working with native threads. I must come > clean and say that I don't like "green" threads. I actually don't want *pre-emptive* threads -- I'd rather have threads suspend at I/O points instead. But yeah, my preferred abstraction is threads; and those user-space threads could be bound to a core, or we could schedule them on multiple cores. But there's room for many things :) > Which leads on to the third point, which is that I would like to see > guile match its words (in its documentation) with respect to native > threads. I have found they work fine, with caution about shared global > state. You think they don't, except possibly on intel, because some of > its lock-free structures/variables -- and I think you are possibly > referring to the VM here -- lack appropriate fences or other atomics. > (The higher level C and scheme code has plainly had serious attempts at > thread-safety made for it using mutexes.) I see what you mean. On the other hand I don't like documenting bugs :/ (I prefer to fix them, and find that often the effort is similar.) Good native thread support is within Guile's remit and everyone should have the good experience that you have had. But that's not something we can guarantee right now unfortunately :/ Andy ^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: anyone define port types? 2016-06-23 10:43 ` Andy Wingo @ 2016-06-23 11:49 ` William ML Leslie 0 siblings, 0 replies; 43+ messages in thread From: William ML Leslie @ 2016-06-23 11:49 UTC (permalink / raw) To: guile-user, guile-devel On 23 June 2016 at 20:43, Andy Wingo <wingo@pobox.com> wrote: > On Thu 23 Jun 2016 11:24, Chris Vine <chris@cvine.freeserve.co.uk> writes: >> Secondly, as I understand it in the end you want pre-emptive "green" >> threads for guile, whereas my code equates to co-operative >> multi-tasking, whilst also working with native threads. I must come >> clean and say that I don't like "green" threads. > > I actually don't want *pre-emptive* threads -- I'd rather have threads > suspend at I/O points instead. But yeah, my preferred abstraction is > threads; and those user-space threads could be bound to a core, or we > could schedule them on multiple cores. But there's room for many things > :) It would be nice to be able to avoid pre-emption at I/O points also, ensuring that turns on the event loop run atomically to completion, and any IO wait happens on the next turn. I guess I can discover any pre-emption at I/O points easily with a dynamic-wind handler or custom loop and go and rewrite them. -- William Leslie Notice: Likely much of this email is, by the nature of copyright, covered under copyright law. You absolutely MAY reproduce any part of it in accordance with the copyright law of the nation you are reading this in. Any attempt to DENY YOU THOSE RIGHTS would be illegal without prior contractual agreement. ^ permalink raw reply [flat|nested] 43+ messages in thread
end of thread, other threads:[~2016-06-23 11:49 UTC | newest] Thread overview: 43+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2016-03-28 19:04 anyone define port types? Andy Wingo 2016-03-28 23:53 ` Matt Wette 2016-04-05 14:06 ` Mark H Weaver 2016-04-05 23:55 ` Matt Wette 2016-06-11 16:50 ` Andy Wingo 2016-03-29 8:52 ` Nala Ginrut 2016-03-30 6:29 ` Panicz Maciej Godek 2016-03-30 11:18 ` Jan Nieuwenhuizen 2016-03-30 17:17 ` Panicz Maciej Godek 2016-03-30 17:53 ` Marko Rauhamaa 2016-03-30 19:02 ` Panicz Maciej Godek 2016-03-30 19:57 ` Marko Rauhamaa 2016-03-31 16:11 ` Barry Fishman 2016-03-31 19:28 ` Marko Rauhamaa 2016-03-30 19:43 ` Jan Wedekind 2016-03-30 20:07 ` Marko Rauhamaa 2016-03-30 21:01 ` Jan Wedekind 2016-03-30 22:44 ` Marko Rauhamaa 2016-03-31 20:42 ` Jan Wedekind 2016-03-31 22:28 ` Marko Rauhamaa 2016-06-11 16:53 ` Andy Wingo 2016-04-01 14:38 ` Ludovic Courtès 2016-06-11 16:57 ` Andy Wingo 2016-04-14 14:08 ` Ludovic Courtès 2016-06-11 17:02 ` Andy Wingo 2016-06-12 8:25 ` Chris Vine 2016-06-19 9:13 ` Andy Wingo 2016-06-19 9:55 ` Marko Rauhamaa 2016-06-19 15:27 ` Andy Wingo 2016-06-19 15:33 ` Chris Vine 2016-06-19 17:48 ` Andy Wingo 2016-06-19 20:09 ` Chris Vine 2016-06-20 3:38 ` William ML Leslie 2016-06-20 6:45 ` Chris Vine 2016-06-20 7:34 ` Andy Wingo 2016-06-20 9:01 ` Chris Vine 2016-06-22 22:44 ` Chris Vine 2016-06-23 7:36 ` Andy Wingo 2016-06-23 8:56 ` Andy Wingo 2016-06-23 9:24 ` Chris Vine 2016-06-23 9:50 ` Marko Rauhamaa 2016-06-23 10:43 ` Andy Wingo 2016-06-23 11:49 ` William ML Leslie
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).