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

Initial version #1

Closed
wants to merge 24 commits into from
Closed

Initial version #1

wants to merge 24 commits into from

Conversation

safareli
Copy link
Owner

@safareli safareli commented Oct 7, 2016

TODO:

  • document structures in examples
  • split tests in logical groups
  • update readme
  • basic tests for example structures
    • Identity
    • List
    • Future
    • Pair
    • Func
  • check laws for example structures
    • Identity
    • List
    • Task
    • Pair
    • Func

rethink:

  • name for foldIfIsOf
  • name for quasi itself

Current coverage is 100%


// instance Applicative (Func a) where
// of :: b -> Func a b
Func.of = (a) => Func((_) => a)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this a few days ago. I'll go ahead and define it in sanctuary-type-classes. :)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yah!

isEmpty,

// createas container for a value which currently has no type.
of: Of.of,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😆

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

make Future weaker (cant express failure) so it's simpler for our demonstrational purposes
t.ok(of(a).equals(Identity.of(a)))
t.eqFL(Identity(of(a)), of(a).traverse((a) => Identity(a), Identity.of))
t.eqFL(of(Identity(of(a))), of(a).extend((a) => Identity(a)))
t.eqFL(of(Identity(of(empty))), empty.extend((a) => Identity(a)))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is (a) => Identity(a) better written Identity?

Copy link
Owner Author

@safareli safareli Oct 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identity besides a -> Identity a is also TypeRep Identity and I wanted to be more explicit that a function is passed as argument, even though Identity would work as well.

Object.assign({}, options, { preset: 'angular' }),
parserOpts,
(err, res) => cb(err, res != null ? res.releaseAs : res)
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this file do?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's referenced from package.json for semantic release stuff

@rpominov
Copy link

rpominov commented Oct 11, 2016

I figured this the best place to discuss this approach in general.

I still don't understand it completely, and so far have one question that bothers me: will we have to consider special empty and of values everywhere? For example, I write some function for my app that accepts a Maybe as an argument, do I have to consider a case that this is a special empty value, and not a real Maybe?

Also for example you have an equals method implemented like this:

Pair.prototype.equals = function(b) {
  return equals(this._1, b._1) && equals(this._2, b._2)
}

But don't we have to consider special values like we do in Pair.prototype.concat here as well?

@safareli
Copy link
Owner Author

safareli commented Oct 11, 2016

I write some function for my app that accepts a Maybe as an argument, do I have to consider a case that this is a special empty value, and not a real Maybe?

if we know that some function f which should take a Maybe value than it should only take the Maybe value and some special value should not be considered (if you get some non-Maybe value then it's probably a bug somewhere)

It's would be useful when function just has constraint on some interface:

//    foldMap :: (Foldable f, Monoid m) => (f a, a -> m) -> m
// 1 - this is the special empty value. we don't need a 
//     function or some dictionary to get empty value
const foldMap = (f, as) => as.reduce((acc, a) => acc.concat(f(a)), empty /*[1]*/ )

foldMap(a => Max(a*a), Cons(1, Cons(2, Cons(3, Nil))))) // Max(9)

// foldMap :: (ChainRec m, Applicative m) => Free i a ~> (i -> m a) -> m a
// 1 - we don't need `TypeRep m` in order to access `chainRec`
// 2 - we don't need `TypeRep m` in order to access `of`
//     once it's used with `chain` or `ap` it will "get" type
Free.prototype.foldMap = function(f) {
  return /*[1]*/chainRec((next, done, v) => v.cata({
    Pure: (x) => map(/*[2]*/of(x), done),
    Lift: (x, g) => map(f(x), compose(done, g)),
    Ap: (x, y) => map(ap(x.foldMap(f), y.foldMap(f)), done),
    Chain: (x, g) => map(x.foldMap(f), compose(next, g)),
  }), this)
}

But if we do foldMap(a => Max(a*a), Nil) we will get empty which will be unsafe if you try to get value from it. and if you pass this result to some other function which excepts Max Number it will get this typeless empty monoid value, means it could "leak" in user land too (I don't like it too).
But i think we can look at it as weak type inference. like in haskell if you have some value which should be a Monoid but type system does not know which monoid it is. but in case of Max(10).concat(foldMap(a => Max(a*a), Nil))) it will not work properly and return Max(10).

About equals, I think Identity.of(a) should not equal to of(a) (but I might be wrong on this)

@rpominov
Copy link

But if we do foldMap(a => Max(a*a), Nil) we will get empty which will be unsafe if you try to get value from it. and if you pass this result to some other function which excepts Max Number it will get this typeless empty monoid value, means it could "leak" in user land too (I don't like it too).

Yeah, this is exactly what I was thinking about. The value we got from foldMap(a => Max(a*a), Nil) can then be passed to equals or just some random function in my app.

@safareli
Copy link
Owner Author

safareli commented Oct 11, 2016

If we have Max.value :: Max Number -> Number which handles empty then:
Max.value(foldMap(a => Max(a*a), Nil)) will return -Infinity so this way it would not "leak"

@rpominov
Copy link

rpominov commented Oct 11, 2016

Right, but anyway seems like with this system we would have to use something like MyType.value(v) before we can safely use v of MyType all the time. This probably will work, but seems too cumbersome especially for people who're not interested in the feature we try to support with this approach.

I mean, suppose I don't need a type that delegates monoid functionality to the wrapped values, but I still will have to do MyType.value(v) all the time, because now special empty and of values are legitimate values of any type per the specification I am writing a generic code against.
Edit: Or maybe semi-generic since I know that v is a value of some specific type MyType at some point.

@DrBoolean
Copy link

This is so rad. Just saying

@safareli
Copy link
Owner Author

@DrBoolean thanks!

I'm thinking on it issue @rpominov pointed out. Currently only solution I see is to only call FL methods on objects but if you want to have get() for example on identity or fork on Task this methods must be static methods. I think this will work better with static-land (I would take a closer look at it)

@safareli safareli closed this May 7, 2017
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

Successfully merging this pull request may close these issues.

None yet

4 participants