Skip to content
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

Idea: Only treat objects without prototypes as StrMaps #704

Open
Avaq opened this issue Dec 30, 2020 · 6 comments
Open

Idea: Only treat objects without prototypes as StrMaps #704

Avaq opened this issue Dec 30, 2020 · 6 comments

Comments

@Avaq
Copy link
Member

Avaq commented Dec 30, 2020

I had this thought the other day, and I'll just note it down here lest I forget. It's just an idea. What do you think?


Sanctuary has always conceptually differentiated between Objects and StrMaps, where an Object is like a struct, can have methods, additional properties on its prototype, etc. and a StrMap is a mapping of keys to values of the same type.

For StrMaps, the prototype of an object is ignored, and only values on the surface of the object are acknowledged. For example:

> S.value ('foo') ({foo: 42})
Just (42)

> S.value ('foo') (Object.create ({foo: 42}))
Nothing

> S.value ('toString') ({foo: 42})
Nothing

For Objects, properties on the prototype are also acknowledged, for example:

> S.prop ('foo') ({foo: 42})
42

> S.prop ('foo') (Object.create ({foo: 42}))
42

> S.prop ('toString') ({foo: 42})
[Function: toString]

This is often one of the toughest concepts to explain to newcomers, I find.


My proposal is that Sanctuary (or sanctuary-def, really) would no longer determine whether an Object is a StrMap based on the types of its values, but instead, very simply, based on whether the object has a prototype. In other words:

const type = Object.getPrototypeOf(x) === null ? 'StrMap' : 'Object'

Users can create Objects without prototypes with Object.assign (Object.create (null), {...props}). Sanctuary could provide a StrMap constructor to make this easier: StrMap ({...props}).

I see the following benefits:

  • The created objects are safer even when accessed by functions that don't ignore object prototypes.
  • Sanctuary's built-in StrMap functions no longer have to go out of their way to ignore object prototypes. They now only operate on objects that don't have them.
  • The distinguishing features between Object and StrMap are no longer purely conceptual, making it easier to teach.
  • TypeErrors can be more precise about their input types, because the Object and the StrMap types no longer have overlap.
  • There's a detectable difference now between a "mixed type StrMap" and an "Object", or an "Object whose values happen to be of the same type" and a "StrMap a".
@dotnetCarpenter
Copy link

This definitely need a StrMap ({...props}) constructor. It is already cumbersome to work with other libraries and being even stricter will not make it less cumbersome. However, if this makes the code base leaner and improve error messages, I am all for it.

But please make it simple to convert a foreign object to a StrMap.

@davidchambers
Copy link
Member

I like this idea!

How would we describe the type of the data constructor?

StrMap :: ??? -> StrMap a

Would S.StrMap ({x: 'abc', y: 123}) throw an exception when evaluated?

@dotnetCarpenter
Copy link

I vote for a way to convert all properties to String but S.Left in the normal case. My thinking is that this might be wrong input that I need to handle but when I have made certain it is not, let me get a StrMap String.

You often get a foreign object such as:

{
  fill: '#f06'
, 'fill-opacity': 0.5
, stroke: '#000'
, 'stroke-width': 10
}

@davidchambers
Copy link
Member

@dotnetCarpenter, are you suggesting that the data constructor's return type should be Either String (StrMap a) or Either Error (StrMap a) rather than StrMap a?

@Avaq
Copy link
Member Author

Avaq commented Aug 16, 2021

Would S.StrMap ({x: 'abc', y: 123}) throw an exception when evaluated?

It might be fine just to have it return a mixed-type StrMap. It's possible to work with mixed-type StrMaps outside of Sanctuary, or using S.unchecked.

@Avaq
Copy link
Member Author

Avaq commented Aug 16, 2021

It's just like the Array constructor; We allow users to do ['abc', 123], but then it's checked for consistency when this value is passed into a Sanctuary function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants