contracts.coffee icon indicating copy to clipboard operation
contracts.coffee copied to clipboard

How to put a contracts on a hashmap's keys and values

Open TobiaszCudnik opened this issue 13 years ago • 8 comments

How to put a contracts on a hashmap's keys and values, for example custom key contract and object values.

Right now it's achivable via a custom function contracts which:

  • assets it's an object
  • checks all keys via object.keys()
  • checks all values in a loop using a custom object contract

Is there/will be a syntax for this? How about contract parameters? Maybe

foo :: HashMap TMyKey, TMyObjValue

Or

foo :: { @TMyKey, TMyObjValue }

TobiaszCudnik avatar Apr 04 '12 14:04 TobiaszCudnik

Don't have syntax for this right now. Closest I think is something like this:

class HashMap
  # non-implementation of a HashMap :)
  get: (key) -> @[key]
  put: (key, val) -> @[key] = val


HashMapC = (keyContract, valContract) ->
  ?{
    get: ((keyContract) -> valContract)
    put: (keyContract, valContract) -> Any
  }

StrHashMap = HashMapC Str, Str

f :: (StrHashMap) -> Any
f = (map) ->
  map.get 4

m = new HashMap

m.put 4, "the number four"
f m # contract violation...

Does this match with what you've been trying?

We definitely need some nicer syntax for stuff like this. I'm leaning towards the parameterisation approach (foo :: MyContract T, V) over a specific new syntax just for HashMaps.

disnet avatar Apr 04 '12 23:04 disnet

I think the problem I was just going to open up a ticket for is somewhat related. I am extending backbone views and models. The preferred way of doing this is

View = Backbone.View.extend
    method: (args) ->

So all the methods are defined in a JSON style. It isn't obvious to me how to add a contract. I can define the method externally, but that leads to some duplication. Perhaps I should add it on after extending?

gregwebs avatar Apr 05 '12 22:04 gregwebs

I think you should be able to do something like:

View :: { 
  method: (C) -> C 
}
View = Backbone.View.extend
  method: (args) -> ...

disnet avatar Apr 08 '12 22:04 disnet

yeah, I know I should be able to do that but then my type definition would be far away from the function location. Backbone views can easily take up 100 lines of code.

gregwebs avatar Apr 09 '12 00:04 gregwebs

Ah, I see. Yeah, there's probably not a great way to do it at the moment.

I'd like to add the ability to define inline contracts with object literals. So one could write:

obj = 
  (Str) -> Str
  meth1: (x) -> ...

  (Num) -> Num
  meth2: (x) -> ...

  Num
  x: 42

This is related to adding support for classes since the grammar here is pretty similar. Hopefully I'll have some time soon to crank this out :)

But this still wouldn't entirely solve your backbone example. If we added inline contracts or tried this today:

spec :: { method : (C) -> C }
spec = { method: (args) -> ... }

View = Backbone.View.extend spec

so the object we pass View.extend is wrapped in a contract. Depending on exactly how extend works you might not get what you expect because there's a conceptual mismatch going on. We really want to put a contract on View not the bit of data (spec) that is used to generate View.

Not sure the best way to address this.

disnet avatar Apr 09 '12 00:04 disnet

method :: (C) -> C
method = (args) -> ...

# create a new copy and manually add methods
View = Backbone.View.extend {}
mkView = () ->
  v = new View
  v.method = method

# call to the outside function
View = Backbone.View.extend
  method : (args...) -> method(args...)

These seem like the only approach where I get a contract next to my function and place the contract on the correct object. Second seems preferable.

gregwebs avatar Apr 13 '12 03:04 gregwebs

This seems to work well. A small amount of manageable boilerplate. I need the context, though, so it looks like:

View = Backbone.View.extend
  method : (args...) -> method(this, args...)

I could apply the context (and maybe I will in the future), but I actually kind of like passing it around.

gregwebs avatar Apr 19 '12 20:04 gregwebs

The style I am using now is

addContractMethods = (singleton) ->
  singleton.method :: (Str) -> Any 
  singleton.method = (str) ->

mkClass contractMethods

gregwebs avatar Sep 23 '12 14:09 gregwebs