Code Monkey home page Code Monkey logo

purescript-foreign's Introduction

purescript-foreign's People

Contributors

abhin4v avatar afcondon avatar andreypopp avatar anttih avatar berdario avatar bsermons avatar chexxor avatar davidchambers avatar felixschl avatar garyb avatar joneshf avatar jordanmartinez avatar kl0tl avatar liamgoodacre avatar matthewleon avatar michaelxavier avatar milesfrain avatar natefaubion avatar paf31 avatar parsonsmatt avatar philopon avatar puffnfresh avatar rightfold avatar tfausak avatar thomashoneyman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

purescript-foreign's Issues

Add instances for List, Tuple and Map?

Maps could be read in from a list of pairs, using the List and Tuple IsForeign instances.

I think there should be a defacto way to read in key-value pairs somehow, but I'm not sure if they should be added to this project or to those specific libraries. I could wrap Map in a newtype, but like I said, I really think we should be able to read Maps.

Parse nested value

{
  "bar": "a",
  "nested": {
    "baz": "b"
  }
}
data Foo = Foo { bar :: String, baz :: String }

I want to pull out baz from nested. It doesn't seem like I can do this without first defining a parser for nested. I suppose you'd have to have a generic json-like ast to work with (like in aeson) to serve as an intermediate parse?

It would be neat if simple records could work with this, but I realize that's a larger puzzle.

OneOf?

Feels like we probably already have a oneOf function that can make sure that not all branches are evaluated at once (to ensure efficient as-needed parsing), since using alt naively can lead to really really slow decoding, but I honestly don't know where it is. Is there something I'm missing that already implements this?:

oneOf :: forall f a
   . Foldable f
  => f (Foreign -> F a)
  -> Foreign
  -> F a
oneOf f js = go (fromFoldable f) js
  where
    go (read : xs) js = do
      case runExcept (read js) of
        Right pv -> pure pv
        Left e -> do
          case runExcept (go xs js) of
            Right pv' -> pure pv'
            Left e' -> throwError (e <> e')
    go Nil _ = do
      throwError $ pure (ForeignError "No more parsers to attempt in oneOf")

If not, I could PR this, but feels like it probably exists as a simple combination of some operators.

Limit what types can be turned into Foreign

My understanding of Foreign is that a Foreign is a JS value. Not all PS types are JS types, right?

So, we should change the toForeign function as follows, to ensure that only base JS types are Foreign.

foreign import toForeign :: forall a. a -> Foreign
-- Change to
foreign import toForeignString :: String -> Foreign
foreign import toForeignNumber :: Number -> Foreign
foreign import toForeignArray :: Array Foreign -> Foreign
foreign import toForeignObject :: StrMap Foreign -> Foreign
foreign import toForeignUnsafe :: forall a. a -> Foreign

Is my understanding wrong?

Create new Nullable and Undefined types

This item is just for discussion.

I think it's worth newtyping Maybe to handle undefined and nullable things separately.

e.g.

data Undefined a = Undefined (Maybe a)
data Nullable a = Nullable (Maybe a)

What do you think?

These types would be defined by purescript-foreign and would each have their own instances for ReadForeign.

There would also be a helper function which could take an Undefined (Nullable a) or something similar, all the way down to a Maybe a, to help handling data which could be both nullable and undefined. For data where null and undefined meant different things, this could be made explicit when handling something of type Undefined (Nullable a).

IsForeign instance for Foreign

Where read = id -- would be helpful so that we can have functions that polymorphically return (IsForeign a) and then if the caller wants to just keep is as a Foreign, they can, but it saves them calling read when they have a preferred type.

No Show instance for NullOrUndefined

I saw #13, but since NullOrUndefined is a purescript newtype wrapping only Maybe, I don't see how this could not have a show instance. On a side note, it should probably also have a newtype instance since there's no smart constructor available and using the actual constructor is somewhat verbose and ugly, much more so than wrap, anyway.

Applicative Validation

I've been playing with applicative validation here and thought it might be a nice fit for the sort of thing Data.Foreign is good for.

Limiting the foreign data type parser to an applicative would enable a couple of nice things:

  • Accumulation of a semigroup of errors
  • Static analysis of "schemas" (maybe by using a free applicative - let's call this a stretch goal)

Also, I don't think we need anything more than applicative validation. More often than not, we're just validating all components before combining them back into a useful value by applying a function.

Anyway, I just wanted to bring this up to spark some discussion.

How to handle anonymous options objects?

Hi. I'm new to purescript (and fairly new to JS, too). I'm interested in writing bindings to a few JS libraries. A common design pattern I've seen in several of these JS libraries is the use of an "options" object like this { showFoo: true, fooLabel: "FOO"} , the behaviors of which are as follows. I'll use the purescript-react package as a basis of description, but it applies to lots of other packages.

  1. None or some properties of the options object are required, and some or all are optional. In the case of react, render is required but getInitialState, componentWillMount etc. are optional.
  2. If an optional property is not specified, a reasonable default behavior is used instead.
  3. The values of different properties can have different types.
  4. For some options, the property's value can have one of several allowed types.

PS-react handles (mostly) this scenario by defining a spec that includes all the optional properties, and the required render property is specified separately. The spec defines all optional properties and gives them default values that emulate the behavior of the react pacakge when the optional properties are not specified.

Is this the canonical way to handle options? For small options objects it seems ok to me because it's reasonably easy to keep the property defaults of the bindings and the default behaviors of the package in sync. Also, redefining the same default options again and again in the generated JS isn't a big deal.

However, for some packages like dygraphs that have an options object with dozens of properties, keeping the binding default values and the package's default behaviors in sync can be a lot harder, and generating large options objects with mostly default options specified seems wasteful.

What is the recommended approach for specifying such options to foreign library calls in purescript? Can it be done in a type-safe but general way that can be applied to all foreign libraries? If not, is there a need for such functionality, and if so, where would the right place to add it be? I was thinking something along the lines of the following, although there might be some mistakes due to my newbishness.

myOpts = opts [ getInitialState myHandler1
              , componentWillMount myHandler2
              ] :: Options ReactOpt

opts :: [Tuple String a] -> Options a

data ReactOpt = GetInitialState Handler1Type | ComponentWillMount Handler2Type | ...

getInitialState = Tuple "getInitialState" <<< GetInitialState
componentWillMount = Tuple "componentWillMount" << ComponentWillMount
....

In this case, opts would be a general function specialized by an ADT defining the available options. The implementation of opts would generate a {} object with the properties specified in the list only to prevent code inflation.

Essentially, I would like guidance from more experienced purescript practitioners on how to handle this kind of options case because I have encountered it when looking at several JS libraries to write bindings for. Thanks very much.

readInt function & IsForeign instance for Int

Now that purescript has Ints, could these be added?

(To determine whether a number is an int in javascript, ES6 will have the Number.isInteger() function; until then there's n % 1 === 0)

Two Foreign examples give 404 on Pursuit

The example section of Foreign package on Pursuit has old (?) links:

Simple Types link refers to JSONSimpleTypes.purs and Arrays link refers to JSONArrays.purs.

The example dir has SimpleTypes.purs and Arrays.purs.

Can't read from FileList

Hey,

Small issue, not sure if you feel this is an error or not: When you have an element like this: the element has a "files" property, that holds a FileList (https://developer.mozilla.org/en-US/docs/Web/API/FileList). You can read from this list just like any array (in JavaScript):

 var file = document.getElementById('fileItem').files[0];

However the function isArray in Data/Foreign.js says this is not an array:

 Object.prototype.toString.call(documetn.getElementById("fileItem").files 
 -> [object FileList]

I solved this in my own project with a simple ffi import, but I thought I'd let you know.

Simon

renderForeignError is not exported

renderForeignError is defined in Data.Foreign, but it doesn't seem to be used anywhere in there, and it's neither exported.

Can you change it to be exported?

Possible API Changes

I've wondered for a while whether it would be a good idea to rethink the API for this module. Given that I'm about to write about it for the book, it's probably a good time to raise my concern.

What if we changed the API as follows:

data ForeignError = 
  = TypeMismatch String
  | NoSuchProperty String
  | ...

type F = V [ForeignError]

class ReadForeign a where
  read :: Foreign -> F a
  • Use V instead of Either.
  • Encourage function composition and applicative validation instead of do notation
  • Provide combinators for things like property access:
(.:) :: Foreign -> String -> F Foreign

API question about ForeignError

Currently ForeignError is quite limiting in what kinds of error a read operation can produce, that is, it covers type errors, but it only covers a parse error (say when parsing a String) IF it's parsing JSON.

In my case, I have a Type field which is parsed from a simple Haskell style string "Concrete -> Concrete". If the parse fails for any reason there isn't a ForeignError that covers this case, as it's not a type error but a syntax error. JSON gets special treatment in this case, but perhaps the JSONError should be generalized to cover other cases?

It seems like TypeMismatch isn't going to be useful in terms of implementing new Foreign instances because all the primitive types are already covered by the Foreign library.

isNullUndefinedOrWhiteSpace function

Could we have a function like this? It's the sort of thing I find very useful when string values are passed from C# / ASP.NET to the browser.

// isNullUndefinedOrWhiteSpace :: Foreign -> Boolean
// Test whether a foreign value is a null, undefined, empty or whitespace string (includes
// being not a string).
// Returns false only if the value is a string with one or more non-whitespace characters.
function isNullUndefinedOrWhiteSpace(s) {
    if (!s) {
        return true;
    }

    if (typeof s !== 'string') {
        return true;
    }

    return s.trim().length === 0;
}

Then presumably we could build on this with a PS function with a similar name, but of type Foreign -> Maybe String.

I'd be happy to submit a PR (but be prepared for this newbie's initial fumblings).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.