3 ms·
This is an interesting topic I have been thinking about. I've come to the conclusion it is difficult to be fair when trying to introduce someone to an idea: A
by geocar 11y ago
This is an interesting topic I have been thinking about.
I've come to the conclusion it is difficult to be fair when trying to introduce someone to an idea:
An APL-er recently mused that palindrome-detection was a good example of array-thinking, specifically: {⍵≡⌽⍵} (or in K: {x~|x})
However another programmer made a quick check of the Rosetta Code[1] and observed almost all of the examples used that same method without "array-thinking".
That is, unless we state that
x == reverse(x)
is succinct in many languages, it is easy be accused of being disingenuous, and we run the risk of letting the argument get away from us: After all, the atom solution actually requires fewer operations than the vector solution:
for (i = 0, n = floor(length(x) / 2); i < n; ++i)
if(x[i] != x[n-(i+1)]) return false;
return true;
and despite being longer, may actually be faster if we ever need to test very large strings.
C# programmers often use Contracts. Sometimes they use "if+printf-debugging", sometimes they use "lots of boilerplate code and class inheritance", and sometimes they invent their own runtime assert+log mechanism. The discussion about how C# programmers prevent programs from entering invalid states should not have to admit this any more than I should have to test for very large palindromes.
That is to say that contrived examples are contrived.
So let us observe that many C# users don't define a method:
delete :: User -> DeletedUser
They could, but they don't. Now why don't they?
[1]: http://rosettacode.org/wiki/Palindrome_detection http://rosettacode.org/wiki/Palindrome_detection
- masklinn 11y ago> They could, but they don't. Now why don't they? One of the bothersome issues is with a `delete :: User -> DeletedUser` method is you now have both a User and a DeletedUser, seemingly as valid as the other. Most languages share the issue too, the type-encoding of state change doesn't really work because an instance in the old state is still accessible, at best it might blow up at runtime. That's a place where affine types (à la Rust) are an improvement, because with `delete(User) -> DeletedUser` you'd completely lose access to the User instance.
- seanmcdirmid 11y agoSome research languages allow for dynamic inheritance in static settings, so ActiveUser -> DeletedUser is a valid transition that can be encoded typefully. However, without affine types and with aliasing, some dynamic checking is obviously needed.
- noblethrasher 11y agoAs I mentioned in my reply to 'geocar, we would need to model the entire state machine, which (usually) means a stack or queue. while(queue.Any()) next_queue.Enqueue(queue.Dequeue().Delete()); So, in this scheme you do lose access to the User instance once you call Delete(). A robust solution would probably involve using the visitor pattern to dispatch the correct Enqueue method of next_queue (i.e. we may have multiple next_queues), but as a side benefit of all that boilerplate, refactoring becomes simple, and you can get concurrency for nearly free.
- noblethrasher 11y agoModeling state transitions using classes is part of the correct solution. A complete solution means that you model the entire state machine, which usually means employing stacks and queues. Oh, and as a side-benefit, you get concurrency for nearly free.