Am So., 14. Juni 2020 um 02:07 Uhr schrieb John Cowan <xxxxxx@ccil.org>: >> This is preferable because no heap allocation would occur >> altogether. > > > I suspect this will be a rare operation (neither Haskell nor Scala has such a thing), and I see little point in optimizing it. Haskell is pure, so it cannot contain any linear-update procedures. If Eithers are used in the sense of value + flag (and not in the sense of success vs failure), toggling the flag is a common operation in many algorithms (think of red-black trees, etc.). As the cost of exporting either-swap both under either-swap and either-swap! is literally zero, I see little point in not including it. :) A specialized implementation can then provide a fast heap-allocation less linear update procedure. > Yes, they can. But the key issue here is what the standard provides for. When R7RS-large is complete, we will then have a fixed list of disjoint types analogous to the list in R7RS-small section 3.2. (Not all the SRFIs introduce such types, of course; generators are procedures and list-queues are (improper) lists, for example.) We keep the line "and all predicates created by define-record-type", and everything is resolved. Until then, I think "disjoint" suffices. I hope we will be able to do much better when the final standard is due. Compiling a fixed list that will break as soon as we add one or two further SRFIs into the mix should be the last resort. But we don't have to come up with a solution right now. >> So what is really going on is that Scheme "types" are dynamic. They >> are values created by `define-record-type' (apart from the few >> predefined values), but these values are not first-class values. So >> the question, whether types are disjoint boils down to the question of >> whether locations are disjoint to each "type value" conceptually >> occupies a location in the store. This location is allocated through >> the evaluation of `define-record-type'. > > > That strikes me as unnecessarily complex. The types string and number are disjoint because any object on which string? answers #t, number answers #f, and vice versa. This is not an operational definition, but it is a definition. Operationally one may think of the type of a string or the type of a number occupying a unique location as well. That said, I don't want to claim that my sketched approach using locations to explain disjoint types is necessarily the best. >> But there is still no definition of `traversable' in the latest draft >> if I am not mistaken. > > > I've rewritten it (using the term "mappable") but it's not easy to be both terse and clear. I will check whether I am able to understand it. :) >> > I think it is more important that ->list functions always return a list, even though the distinction between Just no values and Nothing (and likewise for Either) is lost. Conversion functions are often lossy. >> >> Can you explain why it is important that they always return a list? > > > Because that's what "conversion to foo" procedures are all about: you take something and return a corresponding foo. The string->number procedure is an exception because there simply is no interpretation of "@#$%" as a number. If Maybe had already existed, this would have been an excellent place to use it. string->number is actually the perfect example. Following your reasoning, it would have probably returned +nan.0 for the input "@#$%". Obviously, this is much less useful than what it currently does. The way I would interpret the "conversion to foo procedures" is that a true bijection between two types usually cannot be set up, only some mapping. But even such a mapping can usually only be defined on some suitable subset of the domain. In this sense, "string->number" is first of all defined on the subset of all string representing a number and sense reasonably extended to a total function (by extending the codomain as well). BTW, I don't think it would be excellent place to use Maybes here because `#f' is very well distinguishable from a number, so any further wrapping just makes things more complicated and less efficient. If I cannot persuade you to change `maybe->list', could we at least get an optional argument that defaults (if it really has to) to the empty list, but which could be set to `#f' as well. >> The current conversion function is not only lossy, it loses the >> essentials (namely the distinction between Just and Nothing). > > > Only in the highly specialized case of Just of no values. I agree that there are cases when you want this, corresponding to functions that accept or return no values, but most of the time it won't matter at all. This "most of the time" hardly does good to the beauty of Scheme. >> (define (maybe-unfold stop? mapper successor . seed*) >> (if (appy stop? seed*) (nothing) >> (let* ((res (call-with-values (lambda () (apply mapper seed*)) just))) >> (assume (call-with-values (lambda () (apply successor seed*)) stop?) >> res))) > > > Multiple seeds are already added. > > But what is the point of the call to assume? The purpose of `successor` is to generate the next seed (or batch of seeds), but what's the point of doing so if they will never have `mapper` invoked on them? It might make some sense to call `assert` to check for correctness, but `assume` means the compiler can assume that stop? returns true, without that allowing any optimization. Unless I do not understand. The `assume' is only for clarity; you can remove it without changing the semantics. The point is that `stop?' has to be called n + 1 times according to the `unfold' protocol when you generate something of length n. It only makes sense to add an `unfold' procedure if it follows the general protocol. In itself, it would be too trivial to be worth adding. >> >> (19) I still think that it is an error that the error MUST be >> >> signaled. I still propose to change the wording so that an error >> >> SHOULD be signaled ("encouraged" in the RNRS terminology) as this >> >> would not preclude Schemes offering a fast unsafe execution mode.\ > > > My counterproposal is simply to require the failure continuation to be provided. What do you think of that? That's certainly better. The phrase "an error is signaled" is much less helpful if we cannot identify the error being raised. It's like saying that a C++ implementation should raise an exception on a stack overflow or on a SIGSEGV. You wouldn't be able to do much with the exception. >> If you have code that consumes a Maybe, you can use `maybe-if' to turn >> different data paths (encoded by Maybe) into different code paths. But >> when you also need to know the payload, you would use `maybe-case': >> >> (maybe-case maybe >> ((just x) >> ;; maybe is a just and `x' is bound to the payload, which must be a >> single object in this case >> ...) >> ((nothing) >> ;; maybe is a nothing >> ...)) > > > Ah, I see. This looks much more like cond than case. I'll leave it out for now. I don't mind if you call it `maybe-cond'. I thought that `maybe-case' is better because it is really about a finite number of cases and not arbitrary conditions. Marc