-
Notifications
You must be signed in to change notification settings - Fork 6
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
Divide (closed) Primitives and (opened) Builtins #119
base: master
Are you sure you want to change the base?
Conversation
|> required "time" Iso.decoder | ||
|> required "value" D.value | ||
|> required "time" Iso.decoder | ||
|> required "value" D.value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
related to todo of making implementation less naive
, ("time", Iso.encode x.time) | ||
, ("value", Basics.identity x.value) | ||
, ("time", Iso.encode x.time) | ||
, ("value", Basics.identity x.value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
related to todo of making implementation less naive
type alias ElmStreetNonEmptyList a = (a, List a) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is now required as we can't produce type annotation (a, List a)
- but we can produce annotations using this alias.
src/Elm/Ast.hs
Outdated
| ElmTime -- ^ @Posix@ in elm, @UTCTime@ in Haskell | ||
| ElmValue -- ^ @Json.Encode.Value@ in elm, @Data.Aeson.Value@ in Haskell | ||
| ElmMaybe !TypeRef -- ^ @Maybe T@ | ||
| ElmResult !TypeRef !TypeRef -- ^ @Result A B@ in elm | ||
| ElmPair !TypeRef !TypeRef -- ^ @(A, B)@ in elm | ||
| ElmTriple !TypeRef !TypeRef !TypeRef -- ^ @(A, B, C)@ in elm | ||
| ElmList !TypeRef -- ^ @List A@ in elm | ||
| ElmNonEmptyPair !TypeRef -- ^ @NonEmpty A@ represented by @(A, List A)@ in elm | ||
deriving (Show) | ||
|
||
-- | Buitin types defined by core or 3rd party libraries | ||
-- Included definitions: | ||
-- * @Maybe a@ | ||
-- * @Result a b@ | ||
-- * @List a@ | ||
-- * @Time.Posix@ | ||
-- * @Json.Encode.Value@ | ||
data ElmBuiltin = ElmBuiltin | ||
{ builtinImplType :: !Text | ||
, builtinImplEncoder :: !Text | ||
, builtinImplDecoder :: !Text | ||
, builtinImplParams :: ![TypeRef] | ||
} deriving (Show) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is core of the PR
instance Elm Value where | ||
toElmDefinition _ = DefBuiltin $ ElmBuiltin | ||
{ builtinImplType = "Value" | ||
, builtinImplEncoder = "Basics.identity" | ||
, builtinImplDecoder = "D.value" | ||
, builtinImplParams = [] | ||
} | ||
|
||
instance Elm UTCTime where | ||
toElmDefinition _ = DefBuiltin $ ElmBuiltin | ||
{ builtinImplType = "Posix" | ||
, builtinImplEncoder = "Iso.encode" | ||
, builtinImplDecoder = "Iso.decoder" | ||
, builtinImplParams = [] | ||
} | ||
|
||
instance Elm a => Elm (Maybe a) where | ||
toElmDefinition _ = DefPrim $ ElmMaybe $ elmRef @a | ||
toElmDefinition _ = DefBuiltin $ ElmBuiltin | ||
{ builtinImplType = "Maybe" | ||
, builtinImplEncoder = "elmStreetEncodeMaybe" | ||
, builtinImplDecoder = "nullable" | ||
, builtinImplParams = [elmRef @a] | ||
} | ||
|
||
instance (Elm a, Elm b) => Elm (Either a b) where | ||
toElmDefinition _ = DefPrim $ ElmResult (elmRef @a) (elmRef @b) | ||
|
||
instance (Elm a, Elm b) => Elm (a, b) where | ||
toElmDefinition _ = DefPrim $ ElmPair (elmRef @a) (elmRef @b) | ||
|
||
instance (Elm a, Elm b, Elm c) => Elm (a, b, c) where | ||
toElmDefinition _ = DefPrim $ ElmTriple (elmRef @a) (elmRef @b) (elmRef @c) | ||
toElmDefinition _ = DefBuiltin $ ElmBuiltin | ||
{ builtinImplType = "Result" | ||
, builtinImplEncoder = "elmStreetEncodeEither" | ||
, builtinImplDecoder = "elmStreetDecodeEither" | ||
, builtinImplParams = [elmRef @a, elmRef @b] | ||
} | ||
|
||
instance Elm a => Elm [a] where | ||
toElmDefinition _ = DefPrim $ ElmList (elmRef @a) | ||
toElmDefinition _ = DefBuiltin $ ElmBuiltin | ||
{ builtinImplType = "List" | ||
, builtinImplEncoder = "E.list" | ||
, builtinImplDecoder = "D.list" | ||
, builtinImplParams = [elmRef @a] | ||
} | ||
|
||
instance Elm a => Elm (NonEmpty a) where | ||
toElmDefinition _ = DefPrim $ ElmNonEmptyPair (elmRef @a) | ||
toElmDefinition _ = DefBuiltin $ ElmBuiltin | ||
{ builtinImplType = "ElmStreetNonEmptyList" | ||
, builtinImplEncoder = "elmStreetEncodeNonEmpty" | ||
, builtinImplDecoder = "elmStreetDecodeNonEmpty" | ||
, builtinImplParams = [elmRef @a] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utilize new ElmBuiltin
in instances we provide out of the box.
, time : Posix | ||
, value : Value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
related to todo of making implementation less naive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't point to anything that's obviously wrong.
In general it feels that adding more extensibility in this way could open a can of worms.
But this might be fine if we're the only users of the library (just checked that on hackage elm-street is pretty low in terms of number of downloads https://hackage.haskell.org/packages/search?terms=elm)
I expected that making the library extensible would be pretty hard, if for no other reason then just because you're trying to provide API for creating definitions in some other general-purpose programming language.
The piece that is most unclear to me is "how to add more custom definitions/pre-existing decoders etc. to the generated ElmStree.elm to support more user-defined types"
-- * @Time.Posix@ | ||
-- * @Json.Encode.Value@ | ||
data ElmBuiltin = ElmBuiltin | ||
{ builtinImplType :: !Text |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we change the field prefixes to elmBuiltin? The word Impl doesn't look very informative..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I find the word "Builtin" a bit misleading.
It's feels to me to have the same meaning as "Elm primitive" as in "it's provided by Elm itself".
How about something like ElmLibrary / ElmLibType ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Impl is leftover from development. I did the change incrementally by porting support from simpler to more complicated types to new structure it will need to be removed.
It's feels to me to have the same meaning as "Elm primitive" as in "it's provided by Elm itself".
agree though even this is already improvement over including these things into primitives. The one way in which these things are built in even if they come from 3rd party library (say time which depends on elm-iso8601-date-strings
package) is that their decoders and definition is not generated by elm-street. Including Lib into the name is also option. Though this can be even defined in modules other than the ones generated by elm street as well. This might need some more thinking perhaps.
-- * @Json.Encode.Value@ | ||
data ElmBuiltin = ElmBuiltin | ||
{ builtinImplType :: !Text | ||
, builtinImplEncoder :: !Text |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a user of this API It's not clear from the type what this Text is supposed to be.
Is it name of the function defined somewhere? (Where?)
Can I add my custom definitions in that place?
Is it supposed to be fully qualified?
Although it's clear from the usage examples, it might not be so clear to library users ..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be documented, hover before user will be able to define own things free-rely we need to make it possible to define custom imports for generated modules. So it doesn't make much sense documenting this before that part redesign so whole process could be documented.
toElmDefinition _ = DefPrim $ ElmList (elmRef @a) | ||
toElmDefinition _ = DefBuiltin $ ElmBuiltin | ||
{ builtinImplType = "List" | ||
, builtinImplEncoder = "E.list" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The E. and D. in this definition seem magical. Where are they coming from?
EDIT: Ah, I see they're coming from
import Json.Decode as D exposing (..)
import Json.Decode.Pipeline as D exposing (required)
...
import Json.Encode as E exposing (..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. I agreee this is not nice though. But I would probably rather address that in separate PR
Co-authored-by: Jan Hrcek <[email protected]>
Splitting
ElmPrim
into:ElmPrim
- language level constructsElmBuiltins
- types defined by libraries (not generated by elm-street) likeelm/core
orelm/time
etc.ElmPrim
keeps its original closed design using variants in sum type.ElmBuiltins
is using record/product to make implementation open. This gives elm-street several new features:Elm
instances for types provided by libraryElm
instance for parametrized type at least in manual fashion.Full vocabulary of
ElmDefinition
:DefPrim
- primitive types defined by elm (language itself - not just by core or other lib)DefBuiltin
- ADTs not generated by elm-street (provided by libraries like core or time).DefRecord
- record typesDefType
- ADTs generated by elm-streetExample definitions
Future work to be done
Elm.Generate
module should be extended to make it simpler to include custom imports into generated files. (future PR)Possibly further extensions:
ElmType
giving it support for parametrized types.Related issues:
Dict
andSet
primitive for Map, HashMap, IntMan, Set, HashSet and IntSet #26related closed issues:
Elm
for Data.Aeson.Value #111@jhrcek This is not ready for merge but I figured it would be better to get your feedback as soon as I have working spike. Please feel free to comment. This PR resolves my single biggest frustration of working with elm-street.
@arbus I think you might also give input to this proposal