[etlas] Dhall package format
rahulmutt opened this issue · 16 comments
This issue is to discuss the refinements to the etlas.dhall
format to make it more concise and readable than it already is as we get closer to releasing Etlas v1.6.0.0 which will make etlas.dhall
the default format.
Cc: @jneira - Maintainer of the Dhall integration in Etlas
As a starting point to this discussion, I'll suggest some things that I think might make it cleaner using this sample etlas.dhall as a reference for discussion as it is a non-trivial example of real-world usage.
-
Observe
let prelude = https://raw.githubusercontent.com/eta-lang/dhall-to-etlas/master/dhall/prelude.dhall sha256:47a62403bcd28107cb0a99b05fc4d8bc73f06277c422d059ba9efdd50307abbc let types = https://raw.githubusercontent.com/eta-lang/dhall-to-etlas/master/dhall/types.dhall sha256:d36b3384c6e0dc1837f5740b14eb20b1c51de1cb16a8008ee577d7d8f7ea115e
It makes sense to bind each version of Etlas to a certain version of
dhall-to-etlas
and have these two lines automatically added in by Etlas with perhaps flags to customize these since the Etlas code expects the Dhall data structures to be in a certain way when translating to the internalGenericPackageDescription
type. -
Haskell2010
should be the default language so there should be no release to bind it. Once we release an Eta standard, we can make that the default. -
pkg
andpkgVer
should both be included inside of some standard library for Etlas package format that is auto-let binded similar to (1). -
Observe
base = pkgVer "base" "4.5" "5"
Note that we had to type in
base
twice! And I have to do this for every dependency I have. Does dhall have a way to traverse the the keys of a record generically so that we can make it a mapping from package name to version bounds? Also, perhaps we can use the Ivy notation for managing version bounds - it's compact and avoids the awkward&&
and comparison operations. (i.e."[4.5, 5)"
would mean>= 4.5 && <= 5
. -
updateRepo
should probably also be a built-in. -
Observe
[ types.Extension.DataKinds True , types.Extension.TypeOperators True , types.Extension.TypeFamilies True , types.Extension.FlexibleContexts True , types.Extension.MultiParamTypeClasses True , types.Extension.OverloadedStrings True ]
It is very rare that one decides to disable an extension. Perhaps we should abstract out those
True
's and make disabling happen through a function? Also, I'm not completely familiar with Dhall, but is there any language feature that lets you abstract out the long qualified names - like somehow shortentypes.Extension.DataKinds
toDataKinds
in the context ofextensions
?
Good points! I will comment one by one:
- It makes sense to bind each version of Etlas to a certain version of
dhall-to-etlas
and have these two lines automatically added in by Etlas
Agree, they can be automatically added and set in scope for all config files. Moreover, if user want to shadow them with its own versions it will be able to do it cause let x=1 let x = 2 in x == 2
2.
Haskell2010
should be the default language
5.
updateRepo
should probably also be a built-in.
👍
- About dependencies i was thinking in create some default to let users set somethink like:
let commonDeps = [
deps.default "base" "4.5" "5.0",
deps.any "dhall",
deps.strict "bytestring" "0.11"
]
It would generate:
base >= 4.5 && < 5.0,
dhall -any
bytestring == 0.11
And for libs or executables you could do:
build-depends = commonDeps # [ deps.any "wai-servlet" ]
So no need for record keys (although you could use them). I am afraid that it is not a great improvement in conciseness though.
We even could put in scope "sets" of dependencies to match the more common use cases.
Or we could generate automatically a dhall file with a record with the more used dependencies fixed to the recommended bounds (so it would be a curated dep set) and user could simply pick them from there:
let deps= https://github.com/typelead/etlas-index/dhall/default-dependencies
in
....
build-depends: [deps.base, deps.bytestring, ... ]
The possibilities would be endless 😄
4. Does dhall have a way to traverse the the keys of a record generically so that we can make it a mapping from package name to version bounds? Also, perhaps we can use the Ivy notation for managing version bounds - it's compact and avoids the awkward
&&
and comparison operations. (i.e."[4.5, 5)"
would mean>= 4.5 && <= 5
.
I am afraid that for now dhall has no sense of text comparation. It is so by design, to enforce use of lists Records and Union types, more verbose but safer.
It neither can handle record keys in a generic way cause they are types and there is no reflection or another way to instrospect them. You at most could use associacion lists: (wrong, you can not search by text, we would have to make packages an union type to search in a list){ key = "base", value = ["4.5", "4" ]}
and then use a function to search the key base
6. Perhaps we should abstract out those
True
's and make disabling happen through a function?
I think it would be realizable
6. Also, I'm not completely familiar with Dhall, but is there any language feature that lets you abstract out the long qualified names - like somehow shorten
types.Extension.DataKinds
toDataKinds
in the context ofextensions
?
You always can use let
: let Exts = types.Extension
, i am afraid there is no way to make a "context" implicit without naming it in the dhall file itself (you could add them when reading the file in haskell, like prelude or types).
Like dependencies, or other fields (ghc args), we can add to prelude sets of more common used values.
A caveat about add implicit definitions is you cant use dhall executables to proccess the file.
Otoh, if user want to add an explicit prelude/types the implicit ones would be loaded too (so it would add some overhead and cant be used, cause dhall is strict).
So not sure if it is worth save the two lines imports...
Maybe we can try to simplify as much as possible using only imports and functions and see if it could be enough ergonomic.
Hi, i've changed the defaults in dhall-to-etlas
to match the most common options for eta
projects.
The following options are by default:
default-language
:Haskell2010
default-extensions
:
BangPatterns DataKinds DeriveFoldable
DeriveFunctor DeriveGeneric DeriveTraversable EmptyCase
ExistentialQuantification FlexibleContexts FlexibleInstances
FunctionalDependencies GeneralizedNewtypeDeriving MagicHash
MultiParamTypeClasses MultiWayIf LambdaCase OverloadedStrings
RankNTypes RecordWildCards StandaloneDeriving ScopedTypeVariables
TupleSections TypeFamilies TypeOperators
ghc-options
:-Wall -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates
- New function
GitHubTag-project
that creates the repo info suggested inetlas index
(mapSourceRepo
is not needed)
To make dependencies more ergonomic i am thinking in create dhall files with the packages and their bounds and import them in etlas.dhall
files:
- A
index.dhall
file in https://github.com/typelead/etlas-index with the packages stored in it (likedhall-eta
,wai-servlet
) and thepref-ver
data with the patched packages bounds - An
index.dhall
file for each suitable stackage lts with all their packages fixed to a version - Not sure if we could make something similar for hackage cause it is really, really big
Attached how looks the etlas.dhall
and the equivalent cabal file:
-
I've created dhall files representing etlas index local and patched packages and eta boot packages:
-
We should make a tool to convert server index files (tar or git indexed)/cabal config files in a list of dependencies in dhall format: we can use it for generate auto the files as part of regenerating the index and for generate dhall files for stackage lts's.
-
With that default dependencies, the
etlas.dhall
file looks better:
let prelude =
https://raw.githubusercontent.com/eta-lang/dhall-to-etlas/etlas/dhall/prelude.dhall
let types =
https://raw.githubusercontent.com/eta-lang/dhall-to-etlas/etlas/dhall/types.dhall sha256:a6c967e2f3af97d621c2ec058822f41527e10d4288bd45b191e3a79f2ab87217
let deps =
https://raw.githubusercontent.com/eta-lang/dhall-to-etlas/etlas/dhall/dependencies.dhall sha256:e034fad0030e42d5fb1d21056ced132eaf9ded8adcd84f70bc8eaed0e60b1bfe
let v = prelude.v
let dep = prelude.Dependency.orLater-earlier
let any = prelude.Dependency.any
let project =
prelude.utils.GitHubTag-project
{ owner = "eta-lang", repo = "dhall-eta", version = "1.0.0" }
in project
⫽ { synopsis =
"dhall-eta is a eta library that wraps the haskell implementation of dhall configuration language."
, description =
""
, category =
"Language"
, maintainer =
"atreyu.bbb@gmail.com"
, author =
"Javier Neira Sánchez <atreyu.bbb@gmail.com>"
, extra-source-files =
[ "build.gradle"
, "dhall-eta.cabal"
, "dhall-eta.dhall"
, "examples/build.gradle"
, "examples/src/main/java/org/dhall/eta/example/*.java"
, "gradlew"
, "gradlew.bat"
, "gradle/wrapper/gradle-wrapper.jar"
, "gradle/wrapper/gradle-wrapper.properties"
, "java/build.gradle"
, "java/src/main/java/org/dhall/*.java"
, "java/src/main/java/org/dhall/binary/*.java"
, "java/src/main/java/org/dhall/binary/decoding/failure/*.java"
, "java/src/main/java/org/dhall/common/types/*.java"
, "java/src/main/java/org/dhall/common/types/either/*.java"
, "java/src/main/java/org/dhall/common/types/functor/*.java"
, "java/src/main/java/org/dhall/core/*.java"
, "java/src/main/java/org/dhall/core/constant/*.java"
, "java/src/main/java/org/dhall/core/expr/*.java"
, "java/src/main/java/org/dhall/core/imports/*.java"
, "java/src/main/java/org/dhall/core/imports/hashed/*.java"
, "java/src/main/java/org/dhall/core/imports/types/*.java"
, "java/src/main/java/org/dhall/core/imports/types/url/*.java"
, "proguard.txt"
, "README.md"
, "settings.gradle"
, "src/main/java/org/dhall/eta/*.java"
, "src/test/resources/import/data/foo/bar/*.dhall"
, "src/test/resources/import/success/*.dhall"
]
, license =
types.License.BSD3 {=}
, license-files =
[ "LICENSE" ]
, library =
prelude.unconditional.library
( prelude.defaults.Library
⫽ { build-depends =
[ deps.base
, deps.bytestring
, deps.containers
, deps.contravariant
, deps.cryptonite
, deps.dhall
, deps.eta-java-interop
, deps.megaparsec
, deps.memory
, deps.scientific
, deps.serialise
, deps.text
, deps.transformers
]
# [ dep "dotgen" "0.4.2" "0.5"
, dep "lens-family-core" "1.0.0" "1.3"
, dep "prettyprinter" "1.2.0.1" "1.3"
]
, exposed-modules =
[ "Dhall.Eta"
, "Dhall.Eta.Binary"
, "Dhall.Eta.Context"
, "Dhall.Eta.Core"
, "Dhall.Eta.Core.Java"
, "Dhall.Eta.Import"
, "Dhall.Eta.Parser"
, "Dhall.Eta.Parser.Java"
, "Dhall.Eta.TypeCheck"
, "Dhall.Eta.TypeCheck.Java"
, "Eta.Types"
]
, hs-source-dirs =
[ "src/main/eta" ]
, java-sources =
[ "@classes.java" ]
, other-modules =
[ "Dhall.Eta.Map" ]
}
)
, executables =
[ prelude.unconditional.executable
"dhall-eta-all"
( prelude.defaults.Executable
⫽ { build-depends =
[ deps.base, any "dhall-eta" ]
, hs-source-dirs =
[ "examples/src/main/eta" ]
, main-is =
"Main.hs"
}
)
]
, test-suites =
[ prelude.unconditional.test-suite
"tasty"
( prelude.defaults.TestSuite
⫽ { type =
types.TestType.exitcode-stdio
{ main-is = "Dhall/Eta/Test/Main.hs" }
, build-depends =
[ deps.base
, deps.dhall
, deps.dhall-eta
, deps.directory
, deps.filepath
, deps.tasty
, deps.text
, deps.transformers
]
# [ any "dhall-eta"
, dep "tasty-hunit" "0.9.2" "0.11"]
, hs-source-dirs =
[ "src/test/eta" ]
, other-modules =
[ "Dhall.Eta.Test.Common"
, "Dhall.Eta.Test.Import"
, "Dhall.Eta.Test.Normalization"
, "Dhall.Eta.Test.Parser"
, "Dhall.Eta.Test.TypeCheck"
]
}
)
]
}
Attaching the equivalent cabal file:
dhall-eta.cabal.txt
Also, perhaps we can use the Ivy notation for managing version bounds - it's compact and avoids the awkward
&&
and comparison operations. (i.e."[4.5, 5)"
would mean>= 4.5 && <= 5
.
I think we could change dhall-to-etlas
to use that notation in addition to existing ones, cause versions already are opaque Strings and it will continue being valid dhall
The improvement with dependencies is very nice.
-
Make the project bit a bit more concise.
let project = prelude.utils.GitHubTag-project { owner = "eta-lang", repo = "dhall-eta", version = "1.0.0" } in project ⫽ { synopsis =
to
defaultProject { owner = "eta-lang", repo = "dhall-eta", version = "1.0.0", synopsis = ...
where
defaultProject
is a function that takes a record and does the equivalent behavior. Maybe a built-in? -
Make components a tad more concise.
, executables = [ prelude.unconditional.executable "dhall-eta-all" ( prelude.defaults.Executable ⫽ { build-depends = [ deps.base, any "dhall-eta" ] , hs-source-dirs = [ "examples/src/main/eta" ] , main-is = "Main.hs" } ) ]
to
, executables = [ defaultExecutable "dhall-eta-all" { build-depends = [ deps.base, any "dhall-eta" ] , hs-source-dirs = [ "examples/src/main/eta" ] , main-is = "Main.hs" } ) ]
where
defaultExecutable
is again a built-in. And a similar change for library and test components as well.
The names defaultProject
and defaultExecutable
don't really matter - make them what you wish. The basic idea is to factor out the common stuff and make it more streamlined.
Everything else looks good, now we have to decide how to handle built-ins. @jneira What's your plan on that front?
Yeah, i was thinking in make shorter that ones too, but you cant pass arbitrary records to a function, so you have to choose the set of fields to be filled... we can choose only the required ones or something, i'll take a look.
There is a open issue that is relevant dhall-lang/dhall-lang#382
I am currently adding the ivy notation to dhall-to-etlas
Ivy notation already added so the dependencies would be
let dep = prelude.Dependency.singleInterval
.....
build-depends =
[ deps.base
, deps.bytestring
, deps.containers
, deps.contravariant
, deps.cryptonite
, deps.dhall
, deps.eta-java-interop
, deps.megaparsec
, deps.memory
, deps.scientific
, deps.serialise
, deps.text
, deps.transformers
]
# [ dep "dotgen" "[0.4.2,0.5)"
, dep "lens-family-core" "[1.0.0,1.3)"
, dep "prettyprinter" "[1.2.0.1,1.3)"
]
I've moved dependencies.dhall
to etlas-index
, so it would be auto downloaded with etlas update
. The next step would be change etlas
to replace any remote import referring any repo from ~/etlas/config
with local paths inside ~/etlas
(emitting warnings)
Sounds good. Will you be submitting a PR to etlas-index
with dependencies.dhall
?
@rahulmutt submitted typelead/etlas-index#22!
Otoh i've made some progress in simplify the project and components: i've chosen the basic fields for build etlas project as a subset of actual cabal BuildInfo
:
{ build-depends :
List ./Dependency.dhall
, compiler-options :
./CompilerOptions.dhall
, hs-source-dirs :
List Text
, java-sources :
List Text
, maven-depends :
List Text
, other-modules :
List Text
}
It focuses int the fields more used by etlas projects but if you want to use another one you will have to switch to complete BuildInfo
With this, the etlas.dhall
file is close to the equivalent .cabal
file:
let prelude =
https://raw.githubusercontent.com/eta-lang/dhall-to-etlas/etlas/dhall/prelude.dhall
let types =
https://raw.githubusercontent.com/eta-lang/dhall-to-etlas/etlas/dhall/types.dhall
let deps =
https://raw.githubusercontent.com/jneira/etlas-index/dhall-deps/dhall/dependencies.dhall
let v = prelude.v
let dep = prelude.Dependency.singleInterval
let any = prelude.Dependency.any
let comp = prelude.utils.simpleComponent
in prelude.utils.GitHubTag-simple-project
{ repo-owner =
"eta-lang"
, name =
"dhall-eta"
, version =
"1.0.0"
, synopsis =
"dhall-eta is a eta library that wraps the haskell implementation of dhall configuration language."
, category =
"Language"
, maintainer =
"atreyu.bbb@gmail.com"
, author =
"Javier Neira Sánchez <atreyu.bbb@gmail.com>"
, extra-source-files =
[ "build.gradle"
, .....................................
]
, license =
types.License.BSD3 {=}
, license-files =
[ "LICENSE" ]
, library =
comp.library
( prelude.defaults.SimpleBuildInfo
⫽ { build-depends =
[ deps.base
, deps.bytestring
, deps.containers
, deps.contravariant
, deps.cryptonite
, deps.dhall
, deps.eta-java-interop
, deps.megaparsec
, deps.memory
, deps.scientific
, deps.serialise
, deps.text
, deps.transformers
]
# [ dep "dotgen" "[0.4.2,0.5)"
, dep "lens-family-core" "[1.0.0,1.3)"
, dep "prettyprinter" "[1.2.0.1,1.3)"
]
, exposed-modules =
[ "Dhall.Eta"
, "Dhall.Eta.Binary"
, "Dhall.Eta.Context"
, "Dhall.Eta.Core"
, "Dhall.Eta.Core.Java"
, "Dhall.Eta.Import"
, "Dhall.Eta.Parser"
, "Dhall.Eta.Parser.Java"
, "Dhall.Eta.TypeCheck"
, "Dhall.Eta.TypeCheck.Java"
, "Eta.Types"
]
, hs-source-dirs =
[ "src/main/eta" ]
, java-sources =
[ "@classes.java" ]
, other-modules =
[ "Dhall.Eta.Map" ]
}
)
, executables =
[ comp.executable
( prelude.defaults.SimpleBuildInfo
⫽ { name =
"dhall-eta-all"
, build-depends =
[ deps.base, any "dhall-eta" ]
, hs-source-dirs =
[ "examples/src/main/eta" ]
, main-is =
"Main.hs"
}
)
]
, test-suites =
[ comp.test-suite
( prelude.defaults.SimpleBuildInfo
⫽ { name =
"tasty"
, main-is =
"Dhall/Eta/Test/Main.hs"
, build-depends =
[ deps.base
, deps.dhall
, deps.directory
, deps.filepath
, deps.tasty
, deps.text
, deps.transformers
]
# [ any "dhall-eta", dep "tasty-hunit" "[0.9.2,0.11)" ]
, hs-source-dirs =
[ "src/test/eta" ]
, other-modules =
[ "Dhall.Eta.Test.Common"
, "Dhall.Eta.Test.Import"
, "Dhall.Eta.Test.Normalization"
, "Dhall.Eta.Test.Parser"
, "Dhall.Eta.Test.TypeCheck"
]
}
)
]
}
@jneira This looks beautiful now, thanks! Can you commit these changes into etlas? And also somehow lock-in prelude
and types
for a particular version of Etlas.
@rahulmutt by default it would use the last version of dhall-to-etlas
(1.4.0.0
) instead master.
I would like to fix the existent tests and make some new ones before doing a pr.
@rahulmutt also, do you think that will be interesting to let users use directly dhall-to-etla
and etlas-to-dhall
? We could:
- Upload executables in the project and point out in docs
- Download auto executables in the
etlas update
execution
@jneira Perhaps we can add some etlas
commands instead that can perform the conversion taking an input file/output file?
oh, yeah, using etlas project
, no need to download it. Maybe i will upload the executables, just in case.
I've updated dhall support for 1.26.1 of dhall-to-etlas and etlas: typelead/etlas#108