You could manually type-wrap your arguments when calling basic utilities, but that seems verbose almost all the time. ^_-
And in your approach you'd have to hardcode some coercion in every primitive.
If you're talking about putting the wrapper on the target type, like (rep:as special-coersion-wrapper x), that's still more verbose than special-coercion.x.
Whether you care about that verbosity depends on how often you need special-coercion. I just don't want some rare concept taking up space in the namespace.
Ok, enough generalities in this sideshow. Focus on my other responses :)
And in your approach you'd have to hardcode some coercion in every primitive.
I don't follow. In Arc I never say (coerce x 'sym); I always say sym.x instead. Neither of those is more hardcoded than the other, IMO; sym.x hardcodes the 'sym global binding, while (coerce x 'sym) hardcodes the 'coerce global binding and the 'sym type symbol, but they're both ways of looking up the same separately specified behavior.
...Oh, by "primitive" do you mean a function like 'sym? I think it's just a matter of perspective whether 'sym or 'coerce is the more basic concept. IMO, 'coerce acts primarily as a way to offload some functions into a less convenient namespace (which I could do myself with a verbose naming scheme), and secondarily as a way to add an extra dimension of meaning to type tags when they're not attached to types (which strikes me as suspicious wrt extensibility...).
Hmm, I guess the 'type function itself breaks polymorphism. I wouldn't arbitrarily limit the programmer from using 'type, and I know your 'defgeneric relies on it almost intrinsically, but now I've persuaded myself a bit more that I'd rather avoid dealing in specific type tags whenever possible.
---
Ok, enough generalities in this sideshow. Focus on my other responses :)
Apparently I don't do that. <.< Speaking of which, what was the original topic again? >< (* looks it up*)
:) Depends on how far back you go, but I choose to think it's about whether coerce is worthwhile or can be taken out.
If we can have 2 coercions to function, testify and checkify, every primitive that needs a function now must decide which one it's using. That's what I meant by hardcoding. You're trading off verbosity in setting things up for a new type (which I hope will be relatively painless) with just not being able to override certain decisions. I think I'd err toward being more configurable albeit verbose when a type needs a second coercion.
Uh, I only suggested 'checkify as a less confusing name for 'testify. If a language does include both, then yes, it's a bit arbitrary whether something uses one or the other. :) Orthogonality fail.
I believe the choice between 'testify and 'conversionify is very clear. There's no way 'all and friends would use 'conversionify, and there's no way my hypothetical update to 'copy would use 'testify.
---
You're trading off verbosity in setting things up for a new type (which I hope will be relatively painless) with just not being able to override certain decisions.
If you have a choice between (as test x) and (as check x), it's just as hard to override that decision.
I want to try really hard to have just one coercion per type combination, and let users create new types when they want a different conversion. You point out that's verbose, but it may not be overly so, and at least it's overrideable.
testify vs conversionify (ugh :) may be a case where the primitives themselves need multiple coercions. That's fine :) I'm just saying I'd try really, really hard to avoid it because people now have to keep track of two such things. And I'd definitely try to designate one of them the 'default' coercion like arc already does.
Yeah, I might have called it 'coercify if not for the confusion it would cause. ;) And so far I only have one (niche) use in mind for it, so it doesn't have to be nice and brief.
Actually, it would make more sense to call it "transformify." Both "coerce" and "convert" have the connotation of at least sometimes transforming from one kind of value to another. For my purposes, the function [+ 20 _] is a valid behavior--it certainly makes sense to 'copy a data structure but add 20 to one of its parts--and yet the inputs and outputs of that transformation are of the same kind.
---
may be a case where the primitives themselves need multiple coercions.
Nah, you can resort to (as test x) and (as conversion x), with 'test and 'conversion--or 'transform--being callable types.
This is the same thing I meant when I said "you can make a 'test type to turn 'testify into a coercion."
---
I want to try really hard to have just one coercion per type combination, and let users create new types when they want a different conversion. You point out that's verbose, but it may not be overly so, and at least it's overrideable.
Feels like I'm talking past you a bit.
When users want different conversions, they can already define new "anything -> type-foo" global functions, and those are already extensible/overrideable. There's no need to add a framework for that, even if it does manage to be a tolerably simple framework with a tolerably brief API.
Code that chooses between the super-similar testify.x and checkify.x is just as doomed to being arbitrary and hardcoded as if it were to choose between (as test x) and (as check x). The 'coerce framework doesn't help with this hardcoding issue.
---
And I'd definitely try to designate one of them the 'default' coercion like arc already does.
I don't generally agree with designating one operation as default simply based on the types it manages, like "anything -> type-foo." For a more obvious example, it would be especially silly to identify a default "number x number -> number" operation.
Still, I think we agree on what's important. If the point of operation X is to reshape things to expose their fooish quality, then we don't want an operation Y whose point is also to reshape things to expose their fooish quality.
You draw the line farther toward "reshape things to expose their _____ish quality" and embrace 'coerce as a way to abstract the _____ away. As for me, I suppose I've come to believe _____ is an intrinsically subjective, second-class concept.
At one point I wanted Penknife to have a type type, with operations like list'ish.x, list'ify.x, [inherits list seq], and [new.list a b c], which could then be abstracted into other utilities like [def list my-list a b c]. It's been a while, and now I've kinda given up looking for a great advantage; I think the one plus would be having only a single global variable to attach the list documentation to, and I'm not especially excited about that. ^_^
These days I have the vague notion that a user-defined type which extends all the right things oughta be an equally pure representation of _____ish quality; the correspondence between _____s and types isn't one-to-one.
"You draw the line farther toward "reshape things to expose their _____ish quality" and embrace 'coerce as a way to abstract the _____ away. As for me, I suppose I've come to believe _____ is an intrinsically subjective, second-class concept."
That's a pretty good summary. I really haven't considered that alternative at all, so keep me posted on how it goes :)
Lol, by "second-class concept", I mean something that isn't modeled as a value in the language. My gut reaction is that it takes zero work to pursue that alternative. :-p
But yeah, I do still need to solve the same problems 'coerce solves. My alternative, if it isn't obvious by now, is to have global functions that do (fooify x) and occasionally (transform-as-foo x (fn (fooish-x) fooish-result)).
coerce, ify, transform, these seem really similar to me. Why bother splitting such hairs?
I'm reminded of your earlier statement: The purpose of a coercion isn't entirely explained by "I want something of type X." In these cases, it's "I want a Y of type X," and the Y is what's different. Perhaps you didn't go far enough? If you're going to eschew coerce why not get rid of all trace of the types? Don't call it stringify. Call it serialize. Don't call it transform-to-int, call it parse-number. Don't call it aslist, call it scan. Or something like that. It isn't these names, but good name choices would bring me entirely in your camp :)
Ah, right. ^_^ Yeah, most of the times I "use" these things, I'm just making some other abstract nonsense utility. It could be a while until I have another down-to-earth application to try this stuff on.
coerce, ify, transform, these seem really similar to me.
Well, I don't distinguish "ify" from "coerce" except when it helps tell apart the subtle variants we're talking about in this conversation. :-p The subtle variants wouldn't all go into the same language, I hope.
"Transform," on the other hand, seems more general. Really, 'transform as a type wouldn't mean anything other than "unary function, preferably pure." I could very well be splitting hairs by drawing a distinction between that context and the everyday "function" context. However, I think there are some things I want to treat differently between those cases, like ints (multiplication in function position, constant functions in transform position).
If you're going to eschew coerce why not get rid of all trace of the types?
Actually, that's how I think about it. Coercion is a technique to focus output ranges or widen input ranges so utilities are easier to use. If I consistently want a certain kind of focusing--and only then--I'll put it into a utility.
I name those utilities to correspond not with specific types but with informal target sets, usually based on the extensible utilities they support. However, any given tagged type means little except the extensions associated with it, so I think my approach has a natural tendency toward corresponding names anyway.
Don't call it [this], call it [that].
I think I agree with you there. However, I don't expect to come up with witty names as fast as I change my mind about the designs of things. :) For now I'm happy resorting to cookie-cutter names like 'fooify, 'fooish, and 't-foo, just so it's easier to notice relationships between variables and between informal concepts.
"Coercion is a technique to focus output ranges or widen input ranges.."
That's a nice way to think about it.
"I don't expect to come up with witty names as fast as I change my mind about the designs of things. :) For now I'm happy resorting to cookie-cutter names like 'fooify, 'fooish, and 't-foo"