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

Move all functions to namespaced Type Representative #199

Open
briancavalier opened this issue Oct 30, 2016 · 50 comments
Open

Move all functions to namespaced Type Representative #199

briancavalier opened this issue Oct 30, 2016 · 50 comments

Comments

@briancavalier
Copy link
Contributor

briancavalier commented Oct 30, 2016

Recently, I started updating two libs (creed and most) to support FL 2.0, and added Static Land support to most. In the process, I read this thread that led to standardizing on Type Representatives for static functions like of and empty.

All of that led me to the thought of leveraging Type Representatives more fully. I wanted to put the idea out for discussion to see if it seems interesting:

Rather than many namespaced methods, e.g. x[fl.map](f), FL could move to functions on a single namespaced Type Representative, e.g. x[fl].map(f, x), where x[fl] is the location of x's Type Representative, and fl is the only required FL string/Symbol.

This is obviously a substantial change, but I think it would have several benefits:

  1. It standardizes on functions, rather than a mix of functions and methods.
  2. It reduces the number of namespaced properties on instances to exactly 1, allowing functions on the Type Representative to be non-namespaced (though they could remain namespaced if that's desirable)
  3. It removes the need to use .constructor as the location of the Type Representative. Although, it may also be worth considering whether it's useful to add the Type Representative as Constructor[fl] for cases where where no instance is available.
  4. While it doesn't completely unify FL and Static Land, it does come close:
    • this new, richer Type Representative is effectively a Static Land dictionary, and
    • provides a standard location for Static Land dictionaries. When an instance is available, it reduces the need to explicitly pass Static Land dictionaries around.

I don’t think this has many downsides compared to the current FL 2.0 approach. For example, ergonomics: the prefixed methods required by FL 2.0 are, imho, already fairly unergonomic for app devs. Library devs will endure them in order to get the benefits of being FL compliant. Moving to functions on a single namespaced type representative seems like it retains the same level of ergonomics.

Any FL delegation library such as fantasy-sorcery or Ramda would need to change its dispatching, which would be a breaking change, but the dispatching is no more complex.

What do you think of the idea of having all functions on a single namespaced Type Representative?

@rpominov
Copy link
Member

rpominov commented Oct 30, 2016

Sounds like a great idea! If we do this right it will effectively make fantasy-land as expressive as static-land while keeping all use-cases of fantasy-land. Here is how we could do this:

  • Most of the spec would look like static-land (will focus on describing type representatives, all laws will be expressed in terms of type representatives, etc.). This will eliminate need of static-land to exist, and we can borrow most of it from static-land.
  • At the end it would have a statement like "values may have a special property "fantasy-land" that is a link to a corresponding type representative".

It's important to not require values to have "fantasy-land" property, but only say that values may have it. This way we will not loose important features of static-land:

  • We don't have to do wrapping which allows for much simpler code sometimes (example, also Traversable derivations).
  • We're able to have more than one type representative for the same JavaScript type (example).
  • We're able to implement spec support as a separate package, if an original package's authors don't want to add compatibility for some reason.

This will also make life easier for implementers and specs maintainers, and unite the community around single spec!

@joneshf You've mentioned couple times in chat room that we should merge specs, very curious on your opinion on this.

@joneshf
Copy link
Member

joneshf commented Oct 30, 2016

I'm down to try whatever, especially if it brings FL and SL closer to merging! I don't really understand how the suggestion makes all those things possible, but I trust you all do 😊

@paldepind
Copy link

It's important to not require values to have "fantasy-land" property, but only say that values may have it.

I don't understand this. If values only may have it then one can't rely on values having one.

We're able to have more than one type representative for the same JavaScript type

Can't we get that and still require one of them to be defined at a specific location?

@rpominov
Copy link
Member

rpominov commented Oct 31, 2016

A type may support only static-land style spec or both static-land and fantasy-land styles. It would be the same situation as currently with two specs: some types can support both, some only static-land.

If you support both you get more, like interoperability with Ramda. But if you can't add "fantasy-land" property to values for some reason, but still can expose type representative somehow (just say in the docs where it's at), you still get a lot.

Can't we get that and still require one of them to be defined at a specific location?

Yeah, no problem with that. One of them can be a default.

@briancavalier
Copy link
Contributor Author

If you support both you get more, like interoperability with Ramda. But if you can't add "fantasy-land" property to values for some reason, but still can expose type representative somehow (just say in the docs where it's at), you still get a lot.

I wondered about "may" also. It may be good for the spec (if this idea ends up being adopted) to provide guidance/clarification around this (like you just did :) ), perhaps even strongly recommending the default location for the type representative when it's possible.

@abuseofnotation
Copy link

Hi everyone,

Nice to see some good propositions like this one. Here are my two cents on the issues from briancavalier's first post:

  1. Maybe we shouldn't be so obsessed about the Function/Method dichotomy - in JS methods are functions and vice versa. So currently map can be defined as a function which is accepts a -> b as its first argument and a functor value as its this argument. So that "type representative" thing that you are talking about already exists - its called the prototype. Primitives already have prototypes, but its useful to follow the same convention for them, especially since it may very soon be possible to subclass them!
  2. Namespaced methods suck and the [fl] symbol is a good way to avoid them (even if we don't make everything static). I'd define it as a method which returns a contained object as opposed to a property containing a method dictionary.
  3. That thing about the of function being in the constructor also sucks. For me the most natural thing will be to have it it in the prototype, as everything else. Some functions take the type as a parameter, as opposed to value? OK then, just pass the prototype.

This article is somewhat related to the discussion and it is an interesting read:
http://www.haskellforall.com/2012/05/scrap-your-type-classes.html

@evilsoft
Copy link
Contributor

evilsoft commented Nov 1, 2016

@boris-marinov: #1 does not fly if people are not using this. That would force consumers to always go with prototypes. If I am reading that correctly.

for instance I define my ADTs like:

function IO(run) {
  const map = fn => ...
  ...
  return { run, map, ap, chain, of}
}

so using this is not going to fly for all users.

@rpominov
Copy link
Member

rpominov commented Nov 1, 2016

@boris-marinov The point of this proposal is to unite fantasy-land and static-land. Static-land has some advantages over fantasy-land (see pros in the docs and previous comments here). If we do this right fantasy-land will also support cases that only static-land supports without loosing anything. And we will have only one spec. That's why we need type representatives to play the central role instead of using prototypes.

@abuseofnotation
Copy link

I get that. What I was saying is that an object can be defined by functions that use the this argument but don't rely on the data they receive to have them, exposed as methods. In that way we will have an truly universal definition:

const array = {
  map (f) {
    return array.reduce.call(this, (acc, el) => {
      acc.push(f(el))
      return acc
    }, [])
  },
  reduce(f, acc) {
    for (let i = 0; i < this.length; i++) {
      acc = f(acc, this[i])
    }
    return acc
  },
  chain (f) {
    return array.reduce.call(this, (a, b) => a.concat(f(b)), [])
  }
}

Using with point-free style

As you'd imagine, its not hard to convert a function that uses this to a curried function.
You even get the benefit that you have more information about the function, which you can use to validate the input:

const curryThis = (func) => (...args) => {

  if(args.length !== func.length) {
    throw new TypeError('Wrong number of arguments') 
  } else {
    return  (data) => func.apply(data, args)
  }
}

const curryAll = (functions) => Object.keys(functions)
  .reduce((curriedFunctions, key) => {
    curriedFunctions[key] = curryThis(functions[key])
    return curriedFunctions
  }, {})
let { map, reduce, chain } = curryAll(array)
let compose = (f, g) => (a) => f(g(a))

let doStuff = compose(chain((a) => [a, a]), map(String))
console.log(doStuff([1, 2, 3])) //['1', '1', '2', '2', '3', '3']
map((a) => a, (a => a)) //TypeError('Wrong number of arguments') 

Using with OOP

Creating objects from these type definitions is trivial for non-built in values.Built-ins can also be used with the type descriptor, by using underscore-style wrappers:

const wrap = (functions) =>  {
  const proto = Object.keys(functions).reduce((proto, key) => {
    proto[key] = function(...args) {
      //Just a sample implementation
      //Obviously not all functions return the same type of object as they accept
      return constructor(functions[key].apply(this.value, args))
    }
    return proto
  }, {})
  const constructor = (value) => {
    let object = Object.create(proto)
    object.value = value
    return Object.freeze(object)
  }
  return constructor
}

let ArrayPlus = wrap(array)
console.log(ArrayPlus([1, 2, 3])
  .map((a) => String(a))
  .chain((a) => [a, a])
  .value)
//['1', '1', '2', '2', '3', '3']

Using with the ES7 Bind operator

The best part is that this prototype format will instantly be compatible with the new bind operator.

let { map, reduce, chain } = array
[1, 2, 3]::map((a) => String(a))::chain(a) => [a, a])

That is what I had in mind.

@rpominov
Copy link
Member

rpominov commented Nov 2, 2016

Ah, I see. Yea, bind is cool. Although it's only stage 0. We could also write a converter from the current static-land approach to the bind compatible one:

const bindify = T => {
  return {
    map(f) {
      return T.map(f, this)
    },
    ...
  }
}

let { map, reduce, chain } = bindify(array)
[1, 2, 3]::map((a) => String(a))::chain((a) => [a, a])

Other way around conversion is also possible of course, so the question is what to choose for default. To me current static-land approach seems like a better default so far, but need to think more about it.

@abuseofnotation
Copy link

One thing that you should consider is that you cannot have a generic converter from static-land-style type representative to a bind-compatible one, while you can have the reverse.

@safareli
Copy link
Member

safareli commented Nov 3, 2016

Also one issue I see in FL is that we can't define typeclass which is parameterized with multiple types, Kleisli Functors for example:

class Monad m => KleisliFunctor m f where
  kmap :: (a -> m b) -> f a -> f b

@SimonRichardson
Copy link
Member

🤔

On Thu, 3 Nov 2016, 12:37 Irakli Safareli, notifications@github.com wrote:

Also one issue I see in FL is that we can't define typeclass which is
parameterized with multiple types, Kleisli Functors
http://elvishjerricco.github.io/2016/10/12/kleisli-functors.html for
example:

class Monad m => KleisliFunctor m f where
kmap :: (a -> m b) -> f a -> f b


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#199 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACcaGFn8jb3xjmOQf3u6y5QaxNgYMX0zks5q6dVngaJpZM4KkRUs
.

@syaiful6
Copy link

syaiful6 commented Nov 4, 2016

static method like this maybe good for simple usage of monad, but when you do more than that, say a monad transformer then good luck for user to give correct dictionary for it.

@rpominov
Copy link
Member

rpominov commented Nov 4, 2016

@syaiful6 Can you give an example? I don't understand monad transformers very well yet, but as far as I understand transformer just takes a monad and gives you another monad with some added effect. So can it be just a function that takes one Type Representative and returns another one?

@syaiful6
Copy link

syaiful6 commented Nov 4, 2016

@rpominov Yes, to define the monad maybe simple, but not all. If you try to define a free monad transformer, maybe to support side effecs for your dsl, then you will require 2 dictionary, one for monad (the effects) and one for the functor (the command).

The writer of monad transformer can just define the free monad, the problem will be more complex when another author use the free monad tranformer for their library, maybe a coroutine. for simplicity, purescript coroutine:
https://github.com/purescript-contrib/purescript-coroutines/blob/master/src/Control/Coroutine.purs#L60

here you will require more than 3 all dictionary (2 for functor, 1 monad) - probably more, the order should happen correctly. and the value inside the monad transformer should be correctly the associated with dictionary and value you given on it.

if we just use the current fantasy land, you will just require 1 monad dictionary passed on it, which you can require on top of the module.
here my rewrite based on it, where M is the monad used on free monad transformer. if it static, you will have problem which dictionary should
be used on map, ap, and pure(of).

    const fuseWith = curry(3, (f, fs, gs) => {
      return freet.FreeT(() => go(Tuple(fs, gs)))
      function go(v) {
        let n = ap(
          map(
            liftA2(f(Tuple)),
            parallel(freet.resume(fst(v)))
          ),
          parallel(freet.resume(snd(v)))
        )
        return chain(next => {
          return next.matchWith({
            Left: ({ value }) => pure(M, Left(value)),
            Right: ({ value }) => pure(M, Right(map(t => freet.FreeT(() => go(t)), value)))
          })
        }, sequential(n))
      }
    })

@rpominov
Copy link
Member

rpominov commented Nov 4, 2016

I agree that this seems hard to implement using only Type Representatives, i.e. implement fuseWith against current static-land spec (although not sure, still digesting it).

But this proposal suggests to not only have Representatives but also to have "fantasy-land" property on each value that contains corresponding Representative. With that in mind I don't understand why this example will be harder compared to current fantasy-land. I mean we still can write a map that works with any value:

function map(f, v) {
  return v["fantasy-land"].map(f, v)
}

And then use such map and ap in fuseWith. As for of/pure, again nothing different from current fantasy-land, if we don't have a value nor Representative it's impossible to use of, if we have a value we can do v["fantasy-land"].of(1).

@syaiful6
Copy link

syaiful6 commented Nov 4, 2016

Ah, i got it. I think this suggest us to move all function to static one. It's ok i think if we have both.

but it would require the method happen on two locations? on the prototype and the type? what a pain to write the ADT, think ADT sum type. but it look like good to try, since it have some advadtage.

@abuseofnotation
Copy link

abuseofnotation commented Nov 4, 2016

Actually monad transformers are even simpler to implement with type representatives: Type representatives allow you to change the type of a given value without wrapping it in a container and the abundance of containers is one of the biggest downsides of Monad Transformers.

So for example the type of maybeT in Haskell is

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

where the sole purpose of the MaybeT container is to allow for MaybeT m to have a different monad instance than m:

instance (Monad m) => Monad (MaybeT m) where
    return = lift . return
    x >>= f = MaybeT $ do
        v <- runMaybeT x
        case v of
            Nothing -> return Nothing
            Just y  -> runMaybeT (f y)

Using type representatives, maybeT can be implemented using just (no pun intended :)) one value of type m (Maybe a) and an augmented type representative like this one (this.outer. is the type representative of m):

  // (val) => M({value:val})
  of (val) { return this.outer.of({value: val, isJust:true }) },
  // (val => M({value:val}) , M({value:val})) => M({value:val})
  chain (funk, mMaybeVal) {
    return this.outer.chain((value) => {
      return value.isJust ? funk(value.value) : this.outer.of(value) 
    }, mMaybeVal)
  },

The rest of the code is here

@rpominov
Copy link
Member

rpominov commented Nov 4, 2016

but it would require the method happen on two locations? on the prototype and the type?

No, I guess if we go with this approach methods will live only on Representatives.

@syaiful6
Copy link

syaiful6 commented Nov 4, 2016

But this proposal suggests to not only have Representatives but also to have "fantasy-land" property on each value that contains corresponding Representative.

@rpominov then how the value have "fantasy-land" property without usage of prototype and somehow should be sync on the method live on Representative, right? like Just/Nothing on Maybe?

@boris-marinov i know it simple to define the monad transformer, see my comment above. The problem i see it when use it on another library.

I agree this proposal have some advantages and solve some of current spec i think. I aggree with this propopsal, as long as the value also have "fantasy-land" property on it, and not go static like Static Land spec.

@rpominov
Copy link
Member

rpominov commented Nov 4, 2016

then how the value have "fantasy-land" property without usage of prototype and somehow should be sync on the method live on Representative, right? like Just/Nothing on Maybe?

Sorry, I may have misunderstood the question. prototype still be used. An implementation of a type compatible with the new approach may look like this:

function Id(x) {
  this._x = x
}

const IdRepresentative = {
  of(x) {
    return new Id(x)
  },
  map(f, v) {
    return new Id(f(v._x))
  },
}

Id.prototype['fantasy-land'] = IdRepresentative

// No this stuff any more
// Id.prototype['fantasy-land/map'] = function(f) {...}

@syaiful6
Copy link

syaiful6 commented Nov 4, 2016

@rpominov yes, i think i confused about the term TypeRepresentative, when i read the spec it suggest me it the constructor.

If a type provides a type representative, each member of the type must have a constructor property which is a reference to the type representative.

but then here i think it referred on something else.

@rpominov
Copy link
Member

rpominov commented Nov 4, 2016

Yeah, "Type Representative" in current fantasy-land and in this proposal have slightly different meaning.

In current spec

It's an object like:

{
  'fantasy-land/of'(x) {...},
  'fantasy-land/empty'() {...},
  'fantasy-land/chainRec'(f, i) {...},
  // that's it, only 3 methods
}

That lives on value.constructor, which also happen to be a reference to the constructor if value was created using classes.

In this proposal

It's an object like:

{
  of(x) {...},
  empty() {...},
  chainRec(f, i) {...},
  map(f, v) {...},
  ap(f, v) {...},
  ... // all the rest of the methods also should be here
}

That lives on value['fantasy-land'].

@syaiful6
Copy link

syaiful6 commented Nov 4, 2016

that make sense now, i think this proposal want the fl methods defined on constructor, which i think just like static land, since it will not available on the instance.

Can we still type it using typescript/flow? is it possible to write it the ADT using these typed js? i dont have strong opinion on it, just many people asks the type definitions for some ADT. what's other advantages of this proposal beside the single location of the method? if it just location, then what's really we gain because of it?

@rpominov
Copy link
Member

rpominov commented Nov 4, 2016

Not sure about types yet, but I think situation may actually improve compared to what we have currently with fantasy-land, or at least not get worse. Writing typings for individual types should be possible. But representing something like type classes in Flow/TypeScript is still tricky, but also may become easier with this approach (I guess flow-static-land may be compatible with this new approach). @gcanti may have a better perspective on this.

what's other advantages of this proposal beside the single location of the method? if it just location, then what's really we gain because of it?

This can basically unite two specs. The key is to not require to have fantasy-land property, but only say that values may have it. So if you have a Type Representative but values don't reference it in their fantasy-land property, you still have a spec compatible Type Representative, that is also can be useful. I've explained this in previous comments: #199 (comment) #199 (comment)

@syaiful6
Copy link

syaiful6 commented Nov 4, 2016

@rpominov sorry, i am with @paldepind comment above and doesn't understand it even after reading your comment. but, just go with this proposal. it would make sense to see the actual PR i think.

@rpominov
Copy link
Member

rpominov commented Nov 4, 2016

If you're reading through discussion, please read this comment. This one is important.

I think we should just make a distinction between spec compatible Type Representatives and spec compatible values.

Spec compatible Type Representative is just a dictionary with certain functions, for which certain laws stand, for example F.map(x => f(g(x)), a) ≡ F.map(f, F.map(g, a)).

And spec compatible value is a value that has a 'fantasy-land' property that points to a Type Representative that can work with values of same type. So we won't have to say that spec compatible value may have the property. We will say that it must have it in order to be a fantasy-land compatible value.

Having this two separate artefacts, people may choose to totally ignore values part and write their generic code against Type Representatives. Or they may use values part as well. Choosing one way or another has certain trade-offs described above.

One important detail: TypeRep.of(1)['fantasy-land'] !== TypeRep should be allowed. Representatives should be allowed to produce spec incompatible values, or values that have reference to some other Representative.

Update:

TypeRep.of(1)['fantasy-land'] !== TypeRep should be allowed.

But probably not in the case when we get Representative from a value. In other words this should not be allowed v['fantasy-land'].of(1)['fantasy-land'] !== v['fantasy-land']. We can describe this exception in values part.

Udate 2:

See example https://gist.github.com/rpominov/6b4462137aff8de92dbd078da6a3564c

@gcanti
Copy link

gcanti commented Nov 5, 2016

@rpominov I'd be glad to help with the Flow typings, but I don't understand how all this should work. Perhaps a concrete implementation example, let's say Maybe, would be helpful

@gcanti
Copy link

gcanti commented Nov 5, 2016

Another observation: even with the proposed changes I don't see how to encode in fantasy-land the following two monoids

  • (number, *, 1)
  • (number, +, 0)

without modifying the Number constructor or the Number prototype (or wrap numbers in a class). Moreover I fail to see how to encode both of them at the same time, so I must make an arbitrary choice. static-land seems strictly more powerful in this regard.

@abuseofnotation
Copy link

The idea, I think, is to have a the type definition as a separate argument, so for example an implementation of fold would be:

let fold = (MonoidA) => (listA) => 
  listA.reduce((a1, a2) => MonoidA.concat(a1, a2), MonoidA.id())

fold(sumMonoid)([1, 2, 3, 4])) //10
fold(multiplicationMonoid)([1, 2, 3, 4]) //24

I will leave another link to this article, in case you missed it, because I think it talks about the same thing:
http://www.haskellforall.com/2012/05/scrap-your-type-classes.html

Here are the monoid instances:

const sumMonoid = {
  id() {
    return 0 
  }
  ,concat(a1, a2) {
    return a1 + a2 
  }
}

const multiplicationMonoid  = {
  id() {
    return 1
  }
  ,concat(a1, a2) {
    return a1 * a2 
  }
}

@gcanti
Copy link

gcanti commented Nov 5, 2016

@boris-marinov that is how static-land actually works. But what about fantasy-land? If we admit explicit dictionary passing, then we can just drop fantasy-land for static-land

@rpominov
Copy link
Member

rpominov commented Nov 5, 2016

@gcanti The idea is to unite the two, have a single spec that allows both approaches. See #199 (comment) and also #199 (comment) #199 (comment)

@rpominov
Copy link
Member

rpominov commented Nov 5, 2016

@gcanti
Copy link

gcanti commented Nov 5, 2016

@rpominov That would be great, though from the discussion I'm not sure how. Something like this?

// @flow

const maybeFunctor = {
  map<A, B>(f: (a: A) => B, fa: Maybe<A>): Maybe<B> {
    return fa instanceof Nothing ? fa : new Just(f(fa.value))
  }
}

class Nothing {
  static fantasyLand = maybeFunctor
  map(f) {
    return maybeFunctor.map(f, this)
  }
  fantasyLand() {
    return maybeFunctor
  }
}

class Just<A> {
  value: A;
  static fantasyLand = maybeFunctor
  constructor(value: A) {
    this.value = value
  }
  map<B>(f: (a: A) => B) {
    return maybeFunctor.map(f, this)
  }
  fantasyLand() {
    return maybeFunctor
  }
}

type Maybe<A> = Just<A> | Nothing;

const double = n => 2 * n
const x: Maybe<number> = new Just(1)

// 4 ways to map
x.map(double)
maybeFunctor.map(double, x)
x.fantasyLand().map(double, x)
x.constructor.fantasyLand.map(double, x)

EDIT: ah sorry, I didn't see your last comment before submitting

@rpominov
Copy link
Member

rpominov commented Nov 5, 2016

Yeah, something like this. Pity that Flow doesn't allow 'fantasy-land' as a name for the property:

static 'fantasy-land' = maybeFunctor      
^ literal properties not yet supported

Same issue with current fantasy-land facebook/flow#2482

@Avaq
Copy link
Contributor

Avaq commented Nov 6, 2016

I'd like to provide a different point of view, hopefully alleviating some of the confusion in this thread, and introducing a new idea:

Static Land can have multiple Type Representatives for the same value, which is great; for example we can do Array and ZipArray types which behave differently. The model is that a Representative knows about its values, but values don't know about their Representatives.

The latter is a problem. It forces us to pass around the Representative along with the value every time we call a function which needs to know the Type (and its implementation). Fantasy-Land solves this problem by associating the Representative with the value itself. In v1, values pointed to their Representative through the __proto__ property, convenient because it's assigned automatically by JavaScript*. In v2 we changed it to the constructor property, convenient because it too is assigned automatically by JavaScript*.

* In cases where the type is constructed using JavaScripts inheritance model.

The inconvenience in both cases, however, is that we're sharing a name-space with other stuff. Whether it be the method namespace or static namespace. In order to combat this issue we've prefixed every property which is part of the Type Representative, so it can live in harmony with whatever else lives there. Now we're looking forward to change the location of the type-representative once again, and finally to a destination where the properties don't have to share. What we end up with are Representatives which are completely compatible with Static Land Representatives. It's like we're using Static Land, but giving values some knowledge about their Representatives by association through the fantasy-land property, or wherever we decide to put it.

A more interesting question, to me, is whether we can encode the multiple Type Representatives thing in a useful way. We're stuck in associating a single Type with every value, because that's how we started. But maybe we can do something useful if we associate all types of a value with it; say: Array.prototype['fantasy-land/types'] = [Array, ZipArray]. Possibly allowing for a new breed of dispatching logic.

Edit: I should clarify that the above approach probably won't gain us anything, but it does demonstrate an idea which flows forth out of the point of view I'm trying to get across, where Fantasy Land merely specifies a way to associate values with their Type(s).

@abuseofnotation
Copy link

@gcanti, in your example Isn't it possible to add the type param also to Nothing, so Nothing<string> !== Nothing<Number>
Sorry for being off-topic.

@rpominov
Copy link
Member

There're some discussions going on in static-land's repository, that may be related to this proposal. Would very appreciate any input there!

fantasyland/static-land#32
fantasyland/static-land#34

@rpominov
Copy link
Member

To move this forward I've created a separate repo where we can discuss specifics of the new spec in individual issues. Hopefully nobody minds. Otherwise this issue will became too big.

https://github.com/fantasyland/unified-specification

I'll open couple issues there soon.

@SimonRichardson
Copy link
Member

I'd rather you forked fantasy-land and then done a PR imo.

@rpominov
Copy link
Member

rpominov commented Nov 27, 2016

I would love to, but actually I'm not sure I'm good for the task. I think this'll require basically a rewrite from a whole different perspective. And having wrote static-land I'm not sure I like the result. My english is mediocre, and education in some areas related to the specification also not great. So not sure I should do this, but I want to help in other ways.

Also it feels like we need more discussion on the specifics before any PR. And it won't be manageable to do all of them in this issue, or inside a PR. So I thought having a temporal repository to manage this process is a good idea.

@SimonRichardson
Copy link
Member

SimonRichardson commented Nov 27, 2016 via email

@rpominov
Copy link
Member

Hi all. Could you please vote on this?

In theory, if I'd create a PR for this, would you prefer it to has minimal amount of changes, or would bring most of static-land with itself?

🎉 for as minimal changes as possible,
❤️ for the whole package.

Note that I've worked a lot on the structure and terminology of static-land, and in my opinion it would make sense to use what I've ended up with. If we try to preserve that of current Fantasy Land document the result might be confusing and harder to understand.

Also note that if you vote I will not automatically expect of you to support the PR, just want to know what you think has a higher chance of moving forward.

And I probably won't open a PR right away, there will be a temporary fork, which we'll be able to polish before an attempt on final merge. I just want to understand what is better direction in your opinion.

@rpominov
Copy link
Member

rpominov commented Feb 16, 2018

I've started to prepare repository for a PR here https://github.com/rpominov/fantasy-land. Feel free to open PRs in it for any changes you want to be made before the big PR to the main repository. Ideally, in the final PR, we should discuss only whether we want it to be merged, and decide on all details before.

@davidchambers
Copy link
Member

Further to Roman's point, please add yourself as a watcher of rpominov/fantasy-land if you're interested in following the preliminary work and making suggestions. :)

@paldepind
Copy link

@rpominov Unless I'm mistaken it's impossible to create issues in rpominov/fantasy-land. Is that intentional?

@rpominov
Copy link
Member

@paldepind Sorry, missed your comment. I've turned on issues now, it's probably off by default for forks.

@rpominov
Copy link
Member

I'll probably won't be doing any changes to the repository unless something comes up. But I'm going to wait for any PRs or issues for some time before opening the big PR.

@rpominov
Copy link
Member

Going to open the PR in couple days. I figured opening the PR is not a big deal, we'll still be able to discuss and make changes in the fork, and PR will be updated automatically.

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