DEFAULT with Predicates: The Must-Match Constraint

With some sadness, the experiment for DEFAULT inside of CASE/SWITCH is having to get the axe. I explain the reason there due to the coming influence of predicates.

Note: I will emphasize that these experiments--even if they get backed out--are frequently major contributors in the sense of the facilities they test and keep working. In this case, skippable left enfix quoting.

(The same is true of why COMMA! could be implemented in a day. Its BAR!-based predecessors had stretched over years of sifting out the interactions of invisible expression barriers with all the other constructs.)

The Predicates Taketh Away, But They Also Giveth!

So to bring in a fun new feature, I've added a predicate to DEFAULT itself.

What the predicate means is that you can have an additional test (beyond "being not NULL or VOID!") for when a value shouldn't be overwritten.

>> value: "not an integer"

>> value: default .integer? [10 + 20]
>> value  ; wasn't an integer, changed
== 30

>> value: default .integer? [1000 + 2000]
>> value
== 30  ; was already an integer, no change.

So the behavior is similar to:

if not integer? value [value: 1000 + 2000]

But you're getting more than avoiding the repeat of a variable name. You're actually getting:

all [
     not void? get/any 'value   ; or `defined? 'value`
     not null? :value           ; or `set? 'value`
     not integer? :value
 ] then [
     value: 1000 + 2000
 ]

Question: Should The Predicate Be Backwards?

This depends on how you read it x: default .xxx? [...]

X Should DEFAULT IF .XXX? is TRUE

X Should DEFAULT UNLESS .XXX? is TRUE

Neither the word "IF" nor "UNLESS" appear in DEFAULT, so it's not like there's an obvious answer.

I picked it the UNLESS way because it seems like you'd end up writing shorter code in general if you talk about what you want instead of what you don't want. Almost every condition would need a NOT in it, otherwise. So you save some typing.

But if people find it confusing this way, and really believe changing it would help, I'd be fine with it.

Predicates Are Game-Changers

There was a lot of agony before about whether BLANK! should count as a "set" variable or not. if that's something people might want to control, you have to think of a refinement name for it (DEFAULT/ONLY ?) and it can get very hard to reason about.

This cuts through all the fog. If you don't supply a predicate, the default is .not.blank? e.g. any non-blank value counts as being already set, and isn't defaulted. But blank values are defaulted.

Providing a custom predicate overrides it. This is infinitely more useful.

I think you're going to like where predicates are going, as the haphazard internal non-configurable decision making of constructs is formalized and overridable.

2 Likes

Concise and powerful. Great new feature, I likes!