-
-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support Symbol objects #29
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,11 +38,32 @@ const $$show = '@@show'; | |
// seen :: Array Any | ||
const seen = new WeakSet (); | ||
|
||
// entry :: Object -> String -> String | ||
const entry = o => k => show (k) + ': ' + show (o[k]); | ||
|
||
// sortedKeys :: Object -> Array String | ||
const sortedKeys = o => (Object.keys (o)).sort (); | ||
// comparator :: (Symbol, Symbol) -> ( -1 | 0 | +1 ) | ||
const comparator = ({description: a}, {description: b}) => ( | ||
a === undefined && b === undefined ? 0 : | ||
a === undefined ? -1 : | ||
b === undefined ? +1 : | ||
a < b ? -1 : | ||
a > b ? +1 : | ||
/* otherwise */ 0 | ||
); | ||
|
||
// wellKnownSymbols :: Array Symbol | ||
const wellKnownSymbols = [ | ||
Symbol.asyncIterator, | ||
Symbol.hasInstance, | ||
Symbol.isConcatSpreadable, | ||
Symbol.iterator, | ||
Symbol.match, | ||
Symbol.matchAll, | ||
Symbol.replace, | ||
Symbol.search, | ||
Symbol.species, | ||
Symbol.split, | ||
Symbol.toPrimitive, | ||
Symbol.toStringTag, | ||
Symbol.unscopables, | ||
].filter (x => typeof x === 'symbol'); | ||
|
||
//# show :: Showable a => a -> String | ||
//. | ||
|
@@ -114,6 +135,11 @@ const show = x => { | |
'new String (' + show (x.valueOf ()) + ')' : | ||
JSON.stringify (x); | ||
|
||
case '[object Symbol]': | ||
for (const s of wellKnownSymbols) if (s === x) return x.description; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And then this is just |
||
if (x.description === undefined) return 'Symbol ()'; | ||
return 'Symbol (' + show (x.description) + ')'; | ||
|
||
case '[object RegExp]': | ||
return x.toString (); | ||
|
||
|
@@ -133,19 +159,33 @@ const show = x => { | |
case '[object Array]': | ||
seen.add (x); | ||
try { | ||
return '[' + ((x.map (show)).concat ( | ||
sortedKeys (x) | ||
.filter (k => !(/^\d+$/.test (k))) | ||
.map (entry (x)) | ||
)).join (', ') + ']'; | ||
const keys = new Set (Object.keys (x)); | ||
const entries = x.map ((e, index) => { | ||
keys.delete (String (index)); | ||
return show (e); | ||
}); | ||
for (const k of [...keys].sort ()) { | ||
entries.push (show (k) + ': ' + show (x[k])); | ||
} | ||
for (const k of (Object.getOwnPropertySymbols (x)).sort (comparator)) { | ||
entries.push ('[' + show (k) + ']: ' + show (x[k])); | ||
} | ||
return '[' + entries.join (', ') + ']'; | ||
} finally { | ||
seen.delete (x); | ||
} | ||
|
||
case '[object Object]': | ||
seen.add (x); | ||
try { | ||
return '{' + ((sortedKeys (x)).map (entry (x))).join (', ') + '}'; | ||
const entries = []; | ||
for (const k of (Object.getOwnPropertyNames (x)).sort ()) { | ||
entries.push (show (k) + ': ' + show (x[k])); | ||
} | ||
for (const k of (Object.getOwnPropertySymbols (x)).sort (comparator)) { | ||
entries.push ('[' + show (k) + ']: ' + show (x[k])); | ||
} | ||
return '{' + entries.join (', ') + '}'; | ||
} finally { | ||
seen.delete (x); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -166,3 +166,47 @@ test ('custom @@show method (non-Object object)', () => { | |
identity['@@show'] = () => 'identity :: a -> a'; | ||
eq (show (identity), 'identity :: a -> a'); | ||
}); | ||
|
||
test ('well-known symbols', () => { | ||
eq (show (Symbol.asyncIterator), 'Symbol.asyncIterator'); | ||
eq (show (Symbol.hasInstance), 'Symbol.hasInstance'); | ||
eq (show (Symbol.isConcatSpreadable), 'Symbol.isConcatSpreadable'); | ||
eq (show (Symbol.iterator), 'Symbol.iterator'); | ||
eq (show (Symbol.match), 'Symbol.match'); | ||
eq (show (Symbol.matchAll), 'Symbol.matchAll'); | ||
eq (show (Symbol.replace), 'Symbol.replace'); | ||
eq (show (Symbol.search), 'Symbol.search'); | ||
eq (show (Symbol.species), 'Symbol.species'); | ||
eq (show (Symbol.split), 'Symbol.split'); | ||
eq (show (Symbol.toPrimitive), 'Symbol.toPrimitive'); | ||
eq (show (Symbol.toStringTag), 'Symbol.toStringTag'); | ||
eq (show (Symbol.unscopables), 'Symbol.unscopables'); | ||
}); | ||
|
||
test ('private symbols', () => { | ||
eq (show (Symbol ('x')), 'Symbol ("x")'); | ||
eq (show (Symbol ('')), 'Symbol ("")'); | ||
eq (show (Symbol ()), 'Symbol ()'); | ||
}); | ||
|
||
test ('globally accessible symbols', () => { | ||
eq (show (Symbol.for ('x')), 'Symbol ("x")'); | ||
}); | ||
|
||
test ('object property symbols', () => { | ||
eq (show ({[Symbol ()]: 0, [Symbol ()]: 0}), '{[Symbol ()]: 0, [Symbol ()]: 0}'); | ||
eq (show ({[Symbol ()]: 0, [Symbol ('')]: 0}), '{[Symbol ()]: 0, [Symbol ("")]: 0}'); | ||
eq (show ({[Symbol ('')]: 0, [Symbol ()]: 0}), '{[Symbol ()]: 0, [Symbol ("")]: 0}'); | ||
eq (show ({[Symbol ('x')]: 0, [Symbol ('y')]: 0}), '{[Symbol ("x")]: 0, [Symbol ("y")]: 0}'); | ||
eq (show ({[Symbol ('y')]: 0, [Symbol ('x')]: 0}), '{[Symbol ("x")]: 0, [Symbol ("y")]: 0}'); | ||
eq (show ({[Symbol ('x')]: 0, [Symbol ('x')]: 0}), '{[Symbol ("x")]: 0, [Symbol ("x")]: 0}'); | ||
eq (show ({x: 0, [Symbol ('x')]: 0}), '{"x": 0, [Symbol ("x")]: 0}'); | ||
}); | ||
|
||
test ('array property symbols', () => { | ||
const xs = ['foo', 'bar', 'baz']; | ||
xs.z = 0; | ||
xs[Symbol ('y')] = 0; | ||
xs[Symbol ('x')] = 0; | ||
eq (show (xs), '["foo", "bar", "baz", "z": 0, [Symbol ("x")]: 0, [Symbol ("y")]: 0]'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This representation doesn't evaluate, though. An alternative would be to output There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're quite right. This is, though, consistent with existing behaviour: > show (/x/.exec ('xyz'))
'["x", "groups": undefined, "index": 0, "input": "xyz"]' We could change the representation of non-index properties, but that would be an orthogonal change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure! Feel free to merge this PR by the way, I just wanted to note it. I'm not sure if it warrants an issue and the consequent work. I can already see the edge cases now, so maybe not open this can. :p |
||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be a Set