Last active
July 20, 2022 13:22
-
-
Save rgchris/c2e51153ed4b2b2b44fa03ce1d381ab9 to your computer and use it in GitHub Desktop.
Custom Types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The journey to creating a new object type begins with ... a function | |
// * It doesn't have to be a named function (that is `function name()` | |
// as opposed to `name = function ()`) but there are advantages to this | |
// that are beyond the scope of this comment. | |
// * As a value, `myNumType` here serves double-duty--as a function in | |
// the sense of a passable first-class value, as, say, in Rebol | |
// * --and as a constructor for objects that use `myNumType` as a | |
// prototype | |
// * The value of `this` depends on the context in which the function | |
// `myNumType` is called. If you call it by itself, `this` refers to the | |
// global context (`window` or whatever `globalThis` refers to) | |
// * When called with `new`, `this` refers to a new object that is | |
// returned by `new` (unless you explicitly return something else) | |
// and whose 'default' values refer to `myNumType.prototype` and any | |
// other objects up the prototype chain all the way back to | |
// `Object.prototype` (prototype chain also beyond the scope of these | |
// comments) | |
// | |
function myNumType(value, unit) { | |
this.value = value | |
this.unit = unit | |
} | |
// `Object.assign` is just a fancy way to extend an object where you | |
// can pass the new properties as an object. I just find this a little | |
// cleaner than `myNumType.prototype.property = ...; ...repeat...` | |
// | |
Object.assign( | |
myNumType.prototype, { | |
// `.valueOf` is used by operators for diserning a value they | |
// might understand | |
// | |
valueOf: function () { | |
return this.value * 2 | |
}, | |
// `.toString` affects how a value is formed, not necessarily | |
// serialization, though you can add `.toJSON` these days for | |
// when a value is encountered by `JSON.stringify` | |
// | |
toString: function () { | |
return '' + this.value + this.unit | |
// '' + -- coerces concatenation | |
} | |
} | |
) | |
// Create a new object using `myNumType.prototype` | |
// | |
let num = new myNumType( | |
1, 'em' | |
) | |
// Using `Array.prototype.join` to invoke stringification. | |
// * `num + 2` invokes `.valueOf` and operates on that value, | |
// in this case a number, thus producing a number | |
// | |
console.log( | |
['=>', num + 2, num] | |
.join(' ') | |
) | |
// | |
// => 4 1em | |
// Expected output |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Rebol [ | |
Title: "Custom Type" | |
Author: "Christopher Ross-Gill" | |
Date: 20-Jul-2022 | |
Notes: https://forum.rebol.info/t/could-strings-have-context/587/18 | |
] | |
; Rebol's URL! type is the most hackable type lexically | |
; * strictly starting `word colon` you can follow it with | |
; any non-zero number of non-space characters with the caveat | |
; although historically Rebol has mangled percent-encoded | |
; sequences | |
; * whatever the `word` part is will connect the value to | |
; an associated scheme if transformed to a PORT! value | |
; | |
my-literal-value: my-special-num-type:1em | |
digit: charset "0123456789" | |
letter: charset "abcdefghijklmnopqrstuvwxyz" | |
; MAKE-SCHEME is just a helper, you can view the source to | |
; see what it does | |
; | |
sys/make-scheme [ | |
name: 'my-special-num-type | |
title: "My Special Number Type" | |
; I *think* `state` is the best place to put this prototype | |
; | |
state: make object! [ | |
value: none | |
unit: none | |
] | |
; `init` is called on the new PORT! that is created with | |
; `make port! spec` or some of the higher level actors | |
; that implicitly create a PORT!, such as `open spec` | |
; | |
init: func [ | |
this | |
][ | |
this/state: make state [ | |
parse this/spec/ref [ | |
"my-special-num-type:" | |
copy value some digit | |
copy unit some letter | |
] | |
] | |
] | |
; Note this is not an OBJECT!-based prototype, | |
; MAKE-SCHEME does this transformation to avoid | |
; binding issues | |
; | |
actor: [ | |
select: func [this property] [ | |
get in this/state property | |
] | |
copy: func [this] [ | |
to url! rejoin [ | |
"my-special-num-type:" | |
this/state/value this/state/unit | |
] | |
] | |
] | |
] | |
num: make port! my-literal-value | |
print [ | |
"=>" | |
select num 'value select num 'unit | |
copy num | |
] | |
; | |
; => 1 em my-special-num-type:1em | |
; Expected output |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment