Coder Social home page Coder Social logo

dhall-lang / dhall-haskell Goto Github PK

View Code? Open in Web Editor NEW
902.0 27.0 209.0 9.58 MB

Maintainable configuration files

Home Page: https://dhall-lang.org/

License: BSD 3-Clause "New" or "Revised" License

Haskell 39.61% Nix 0.93% Dhall 57.18% Shell 0.03% Roff 0.06% CSS 0.07% JavaScript 0.03% HTML 2.08%
configuration-language haskell dhall

dhall-haskell's Introduction

dhall-haskell

You will probably want to read the language-agnostic README here:

This repository focuses on the Haskell bindings to Dhall and contains the following packages:

Navigate to each package's directory for their respective READMEs

Pre-built binaries

You can download pre-built binaries for Windows, OS X and Linux on the release page:

You can also install binaries for OS X using brew, like this:

$ brew install dhall-json

You can also install pre-built Linux binaries for master using Nix using Nix's channel mechanism by following the instructions at this link:

To install the Nix build products without a channel, configure your machine to use cache.dhall-lang.org, as described in the nix section and then visit one of the following links:

You can then click the "Help" button in the bottom right corner, which will show you a nix-env command that you can run to install the prebuilt executable.

If you have the jq command-line tool installed then you can do this in one command by running:

$ nix-env -i "$(curl -L https://hydra.dhall-lang.org/job/dhall-haskell/master/linux-dhall/latest/api/get-info | jq -r .outPath)"

These instructions also work for any pull request, too, by replacing master with the pull request number for any of the above URLs.

Pre-built containers

Prebuilt containers for the latest release available from Docker Hub:

... but if you want to obtain containers for bleeding-edge builds, you can download image archives for each package using the following URLs:

You can then load and run one of these archives like so:

$ NAME="dhall"

$ curl --location --remote-name "https://hydra.dhall-lang.org/job/dhall-haskell/master/image-${NAME}/latest/download/1/docker-image-${NAME}.tar.gz"

$ docker load < "docker-image-${NAME}.tar.gz"
...
Loaded image: dhall:vx95jiijmp2i07f5ynl8h6sllf34jggz

$ docker run --interactive --rm dhall:vx95jiijmp2i07f5ynl8h6sllf34jggz dhall <<< '2 + 2'
4

These instructions also work for any pull request, too, by replacing master with the pull request number for any of the above URLs.

Building from source

For all of the following instructions, make sure to first check out the dhall-lang submodule:

$ git submodule init
$ git submodule update

You can build all of the packages by running:

$ cabal new-build all

And each of them with cabal new-build <package-name>, for example:

$ cabal new-build dhall

... or you can run cabal new-build within each package directory.

You will probably want to use the shared caches hosted at cache.dhall-lang.org and dhall.cachix.org when doing Nix development. This is not required, but this will save you a lot of time so that you don't have to build as many dependencies from scratch the first time.

If your operating system is NixOS then you can add the cache using these NixOS configuration options to access dhall packages from your declarative configuration file:

  nix = {
    binaryCaches = [
      "https://cache.nixos.org"
      "https://cache.dhall-lang.org"
      "https://dhall.cachix.org"
    ];

    binaryCachePublicKeys = [
      "cache.dhall-lang.org:I9/H18WHd60olG5GsIjolp7CtepSgJmM2CsO813VTmM="
      "dhall.cachix.org-1:8laGciue2JBwD49ICFtg+cIF8ddDaW7OFBjDb/dHEAo="
    ];
  };

If you are not using NixOS, then instead modify your /etc/nix/nix.conf file by adding the following options.

Using Nix 2.0 or later:

trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= cache.dhall-lang.org:I9/H18WHd60olG5GsIjolp7CtepSgJmM2CsO813VTmM= dhall.cachix.org-1:8laGciue2JBwD49ICFtg+cIF8ddDaW7OFBjDb/dHEAo=
substituters = https://cache.nixos.org https://cache.dhall-lang.org https://dhall.cachix.org

Using earlier Nix versions (i.e. Nix 1.*):

binary-cache-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= cache.dhall-lang.org:I9/H18WHd60olG5GsIjolp7CtepSgJmM2CsO813VTmM= dhall.cachix.org-1:8laGciue2JBwD49ICFtg+cIF8ddDaW7OFBjDb/dHEAo=
binary-caches = https://cache.nixos.org https://cache.dhall-lang.org https://dhall.cachix.org

Since many tests require HTTP access, you should also add this setting to your /etc/nix/nix.conf:

sandbox = false

You can build all of the packages by running:

$ nix-build

... or you can run nix-build within each package's respective directory to build just that one package.

You can install all of the packages by running:

$ nix-env --install --file default.nix

... or you can run the same command within each package's respective directory to install just that one package.

If you prefer installing the binaries locally in a nix shell environment instead, just run nix-shell in the top-level directory. This option provides additional flexibility with respect to overriding some of the default parameters (e.g. the compiler version), which makes it particularly useful for developers.

You can develop any package by navigating to that package's directory and running:

$ nix-shell
[nix-shell]$ cabal configure
[nix-shell]$ cabal build
[nix-shell]$ cabal test

... or you can add nix: True to your ~/.cabal/config file and then you can run the same cabal commands without an explicit nix-shell:

$ cabal configure
$ cabal build
$ cabal test

You can build all of the packages with

$ stack build

And each of them with stack build <package-name>, for example:

$ stack build dhall-json

Profiling

Say you want to profile the dhall-to-json executable.

Build the containing package with profiling options:

$ stack build --profile --library-profiling dhall-json

Run the executable on your input. For example:

$ stack exec --profile --rts-options -p -- dhall-to-json <<< 'True && False'

This generates a dhall-to-json.prof file in your current directory.

Build the website

If you don't need to change the GHCJS code, then switch to the dhall-lang repository and follow these instructions instead:

If you do need to test changes to the GHCJS code (i.e. the ./dhall-try subdirectory) then stay within this repository, but edit the dhall/dhall-lang submodule to make the following change:

diff --git a/release.nix b/release.nix
--- a/dhall/dhall-lang/release.nix
+++ b/dhall/dhall-lang/release.nix
       let
         json = builtins.fromJSON (builtins.readFile ./nixops/dhall-haskell.json);
 
-        dhall-haskell =
-          pkgs.fetchFromGitHub {
-            owner = "dhall-lang";
-
-            repo = "dhall-haskell";
-
-            inherit (json) rev sha256 fetchSubmodules;
-          };
+        dhall-haskell = ../..;
 
       in
         import "${dhall-haskell}/default.nix";

... and then build the website by running:

$ nix build --file dhall/dhall-lang/release.nix website

... which will incorporate any GHCJS-related changes you make

Contributing

Read the following guide if you would like to contribute:

dhall-haskell's People

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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dhall-haskell's Issues

Improve integration with lens

AFAIK in its current form, if I want to re-use records with lenses, I have two options:

  • force all keys for records to start with _ in configuration files
  • append all keys with a prefix that will match the Haskell record

As a note using makeLensesWith abbreviatedFields won't work with records containing Vector field.

The first option is the easiest path. It is annoying in the sense that end-users will face _ in all record keys.

The second option not only forces the use of a namespace for each fields. For instance for such a record in a file 'box':

{ userName           = ""                                          
, userEmail            = ""                                         
, userStacks           = ["bos", "irisbox"]                        
, eclipsePlugins     = True                                        
...
}

It would be converted into:

{ boxUserName         = ""                                          
, boxUserEmail        = ""                                         
, boxUserStacks         = ["bos", "irisbox"]                        
, boxEclipsePlugins   = True                                        
....
}

But it also forces the name of the 'prefix' to match the name of the haskell record exactly. In this example, if I want to use BoxConfig instead of just Box in the haskell code I will be out of luck.

Is it possible to imagine that the Interpreter would be clever enough to optionally ignore 'underscore' from haskell record ?

data BoxConfig
  = BoxConfig
  { _userName        :: LText
  , _userEmail       :: LText
  , _userStacks      :: Vector LText
  , _eclipsePlugins  :: Bool
  } deriving (Generic, Show)

makeLenses ''BoxConfig
instance Interpret BoxConfig

main = do
  box_config <- input auto "./box"
  let test = box_config^.userEmail.strict

with

{ userName         = ""                                          
, userEmail          = ""                                         
, userStacks         = ["bos", "irisbox"]                        
, eclipsePlugins   = True  
}

Question: How to remove duplicate elements in a List of Text

Is there a way to remove duplicate elements in a List of Text?

Consider the following code. You can interpret the List (List Text) as a list of dependencies each with their transitive dependencies. How can we combine the lists while removing duplicate elements in the result?

let concat = https://ipfs.io/ipfs/Qmbh2ifwcpX9a384vJMehySbV7rdvYhzVbL5ySs84k8BgY/Prelude/List/concat
in concat Text
[ [ "abc", "def" ]
, [ "abc", "ghi" ]
]

The only way I can think of accomplishing this is to have a convention where you associate a string like "abc" with a canonical numeric representation ("abc" is 0, "def" is 1, etc.) and make sure you maintain those associations throughout the code. Then you can remove entries that have a matching canonical number. However, that seems pretty tedious and error prone since it relies on coding discipline to accomplish.

Is there a better way to achieve this?

Thanks!

Remove Haskell from user documentation

@Gabriel439 I love this work, so just some background how to attract more users :)

I think you should remove Haskell from all the docs and move it to a section called something like Haskell integration.

Currently the basic example uses Haskell in the tutorial, this will scare people away and give them a feeling you need to be a Haskell developer to use Dhall.

I'd start with renaming the repository to just Dhall.

I can help with the docs if you agree :)

Pretty print Dhall expressions

I'd like to be able to output Dhall expressions in a pretty-printed format, mainly for automatically formatting Dhall configuration files

trailing commas

If a format should be human-editable, trailing commas in lists & attrsets should be valid.
Right now it aborts with a confusing/unfitting error message:

→ echo "[1,2,3,]" | dhall
(stdin):1:1: error: expected: "\8704",
    "\955", "\\", "forall", "if",
    "let", "merge"
[1,2,3,] 
^        
→ echo "{foo=2,bar=4,}" | dhall
(stdin):1:1: error: expected: "[",
    "\8704", "\955", "\\", "forall",
    "if", "let", "merge"
{foo=2,bar=4,} 
^       

Dhall to Cabal

I think a dhall-to-cabal library would be great. You would write a dhall record and then run the utility and it would generate you a cabal file. This would allow you to use dhalls abstraction capabilities to write shorter and easy to read dhallcabal files.

There are two ways to accomplish this. You could write a library similar to dhall-nix or you could use the new string interpolation to write an almost totally dhall package!

I am interested in doing this but I have been pressed for time currently at work if no one else does it I will get around to in in a couple weeks. This being said I am curious what you all think the best course of action would be to accomplish this.

On a completely unrelated note that doesn't deserve its own issue. Gabriel, you should consider making dhall a github organization. It is super easy and would allow for you to bless things like dhall-rs, dhall-scala, and dhall-cabal.

Provide a pure version of `input`

Hi! I am currently working on a project whichin I would love to use dhall to improve its customization power. However, in order to do so without breaking too many things, I was wondering if it would be possible to use dhall script in pure functions.

That is, I would love to be able to load and parse the script once, then run it in a pure way several time with different arguments.

Do you think it is possible?

Improve parser performance

Parsing is a huge performance bottleneck for the dhall compiler right now

For example, the following ~4 KB file about 2 seconds the dhall executable to process:

λ(xs : List { cores : Natural, host : Text, key : Text, mandatoryFeatures : 
List Text, platforms : List < AArch64_Linux : {} | ARMv5tel_Linux : {} | 
ARMv7l_Linux : {} | I686_Cygwin : {} | I686_Linux : {} | MIPS64el_Linux : {} | 
PowerPC_Linux : {} | X86_64_Cygwin : {} | X86_64_Darwin : {} | X86_64_FreeBSD : 
{} | X86_64_Linux : {} | X86_64_Solaris : {} >, speedFactor : Natural, 
supportedFeatures : List Text, user : Optional Text })  List/fold { cores : 
Natural, host : Text, key : Text, mandatoryFeatures : List Text, platforms : 
List < AArch64_Linux : {} | ARMv5tel_Linux : {} | ARMv7l_Linux : {} | 
I686_Cygwin : {} | I686_Linux : {} | MIPS64el_Linux : {} | PowerPC_Linux : {} | 
X86_64_Cygwin : {} | X86_64_Darwin : {} | X86_64_FreeBSD : {} | X86_64_Linux : 
{} | X86_64_Solaris : {} >, speedFactor : Natural, supportedFeatures : List 
Text, user : Optional Text } xs Text (λ(x : { cores : Natural, host : Text, 
key : Text, mandatoryFeatures : List Text, platforms : List < AArch64_Linux : 
{} | ARMv5tel_Linux : {} | ARMv7l_Linux : {} | I686_Cygwin : {} | I686_Linux : 
{} | MIPS64el_Linux : {} | PowerPC_Linux : {} | X86_64_Cygwin : {} | 
X86_64_Darwin : {} | X86_64_FreeBSD : {} | X86_64_Linux : {} | X86_64_Solaris : 
{} >, speedFactor : Natural, supportedFeatures : List Text, user : Optional 
Text })  λ(y : Text)  (Optional/fold Text x.user Text (λ(user : Text) 
 user ++ "@" ++ x.host ++ "") x.host ++ " " ++ (merge { Empty = λ(_ : {}) 
 "", NonEmpty = λ(result : Text)  result } (List/fold < AArch64_Linux : 
{} | ARMv5tel_Linux : {} | ARMv7l_Linux : {} | I686_Cygwin : {} | I686_Linux : 
{} | MIPS64el_Linux : {} | PowerPC_Linux : {} | X86_64_Cygwin : {} | 
X86_64_Darwin : {} | X86_64_FreeBSD : {} | X86_64_Linux : {} | X86_64_Solaris : 
{} > x.platforms < Empty : {} | NonEmpty : Text > (λ(element : < AArch64_Linux 
: {} | ARMv5tel_Linux : {} | ARMv7l_Linux : {} | I686_Cygwin : {} | I686_Linux 
: {} | MIPS64el_Linux : {} | PowerPC_Linux : {} | X86_64_Cygwin : {} | 
X86_64_Darwin : {} | X86_64_FreeBSD : {} | X86_64_Linux : {} | X86_64_Solaris : 
{} >)  λ(status : < Empty : {} | NonEmpty : Text >)  merge { Empty = 
λ(_ : {})  < NonEmpty = merge { AArch64_Linux = λ(_ : {})  
"aarch64-linux", ARMv5tel_Linux = λ(_ : {})  "armv5tel-linux", ARMv7l_Linux 
= λ(_ : {})  "armv7l-linux", I686_Cygwin = λ(_ : {})  "i686-cygwin", 
I686_Linux = λ(_ : {})  "i686-linux", MIPS64el_Linux = λ(_ : {})  
"mips64el-linux", PowerPC_Linux = λ(_ : {})  "powerpc-linux", X86_64_Cygwin 
= λ(_ : {})  "x86_64-cygwin", X86_64_Darwin = λ(_ : {})  
"x86_64-darwin", X86_64_FreeBSD = λ(_ : {})  "x86_64-freebsd", X86_64_Linux 
= λ(_ : {})  "x86_64-linux", X86_64_Solaris = λ(_ : {})  
"x86_64-solaris" } element | Empty : {} >, NonEmpty = λ(result : Text)  < 
NonEmpty = (merge { AArch64_Linux = λ(_ : {})  "aarch64-linux", 
ARMv5tel_Linux = λ(_ : {})  "armv5tel-linux", ARMv7l_Linux = λ(_ : {})  
"armv7l-linux", I686_Cygwin = λ(_ : {})  "i686-cygwin", I686_Linux = λ(_ : 
{})  "i686-linux", MIPS64el_Linux = λ(_ : {})  "mips64el-linux", 
PowerPC_Linux = λ(_ : {})  "powerpc-linux", X86_64_Cygwin = λ(_ : {})  
"x86_64-cygwin", X86_64_Darwin = λ(_ : {})  "x86_64-darwin", X86_64_FreeBSD 
= λ(_ : {})  "x86_64-freebsd", X86_64_Linux = λ(_ : {})  
"x86_64-linux", X86_64_Solaris = λ(_ : {})  "x86_64-solaris" } element) ++ 
"," ++ result | Empty : {} > } status : < Empty : {} | NonEmpty : Text >) < 
Empty = {=} | NonEmpty : Text >) : Text) ++ " " ++ x.key ++ " " ++ Integer/show 
(Natural/toInteger x.cores) ++ " " ++ Integer/show (Natural/toInteger 
x.speedFactor) ++ " " ++ (merge { Empty = λ(_ : {})  "", NonEmpty = 
λ(result : Text)  result } (List/fold Text x.supportedFeatures < Empty : {} 
| NonEmpty : Text > (λ(element : Text)  λ(status : < Empty : {} | NonEmpty 
: Text >)  merge { Empty = λ(_ : {})  < NonEmpty = element | Empty : {} 
>, NonEmpty = λ(result : Text)  < NonEmpty = element ++ "," ++ result | 
Empty : {} > } status : < Empty : {} | NonEmpty : Text >) < Empty = {=} | 
NonEmpty : Text >) : Text) ++ " " ++ (merge { Empty = λ(_ : {})  "", 
NonEmpty = λ(result : Text)  result } (List/fold Text x.mandatoryFeatures < 
Empty : {} | NonEmpty : Text > (λ(element : Text)  λ(status : < Empty : {} 
| NonEmpty : Text >)  merge { Empty = λ(_ : {})  < NonEmpty = element | 
Empty : {} >, NonEmpty = λ(result : Text)  < NonEmpty = element ++ "," ++ 
result | Empty : {} > } status : < Empty : {} | NonEmpty : Text >) < Empty = 
{=} | NonEmpty : Text >) : Text) ++ "\n") ++ y) ""

When I benchmark the dhall program running on the above file, all of the bottlenecks are parsing-related:

	Sat Aug 26 11:42 2017 Time and Allocation Profiling Report  (Final)

	   dhall +RTS -p -RTS

	total time  =        6.90 secs   (6897 ticks @ 1000 us, 1 processor)
	total alloc = 3,784,992,880 bytes  (excludes profiling overheads)

COST CENTRE            MODULE                  SRC                                              %time %alloc

noted                  Dhall.Parser            src/Dhall/Parser.hs:(120,1)-(124,44)              23.6   20.8
token                  Dhall.Parser            src/Dhall/Parser.hs:105:5-57                      21.7   21.9
buildSomeSpaceParser   Text.Parser.Token.Style src/Text/Parser/Token/Style.hs:(110,1)-(132,26)    5.4    5.4
reserve                Dhall.Parser            src/Dhall/Parser.hs:(141,1)-(143,13)               5.3    5.2
exprC.chain            Dhall.Parser            src/Dhall/Parser.hs:(485,5)-(487,79)               5.0    4.5
symbol                 Dhall.Parser            src/Dhall/Parser.hs:(146,1)-(148,13)               4.1    4.3
uncons                 Data.ByteString.UTF8    Data/ByteString/UTF8.hs:(166,1)-(167,38)           3.5    5.6
exprF                  Dhall.Parser            src/Dhall/Parser.hs:(527,1)-(725,16)               2.2    1.7
exprF.exprParseDouble  Dhall.Parser            src/Dhall/Parser.hs:(698,5)-(703,35)               2.0    1.4
decode                 Data.ByteString.UTF8    Data/ByteString/UTF8.hs:(68,1)-(124,32)            1.9    6.6
manyAccum              Text.Trifecta.Parser    src/Text/Trifecta/Parser.hs:(183,1)-(187,47)       1.7    1.0

I know that we can do better because that parsing speed is two bytes per millisecond, which is definitely too slow

[Question] Best strategy for converting Data.Text.Lazy into Data.Text

Dhall exposes Data.Text.Lazy as Text while most haskell modules (and prelude) will use the strict version by default.

  1. What would be the best strategy to hide the conversion boiler plate ?
  2. Why is dhall defaulting to the lazy version while - for most configuration purpose - I would expect most 'string' to be kind of short.

Thanks for your help

Type ascription precedence inconsistency

Typed lists and optionals are a different syntax construct than normal type ascription and bind tighter than expected:

\(x : Natural) -> [x] : List Natural
∀(x : Natural) → List Natural

λ(x : Natural) → [x] : List Natural
\(x : Natural) -> x : Natural
^D
Use "dhall --explain" for detailed errors

Error: Expression doesn't match annotation
\(x : Natural) -> (x : Natural)
∀(x : Natural) → Natural

λ(x : Natural) → x

The function arrow also binds tighter than Haskell’s, although that’s only a bit suprising and not an internal inconsistency:

\(x : Natural) -> x : Natural -> Natural
Natural → Natural

λ(x : Natural) → x

I ran into this while trying to port the new unannotated list syntax to my Dhall implementation in Rust. I’m using the LALRPOP library for parsing and hit a shift/reduce conflict between the annotated list rule and the top level type ascription rule after parsing an '[' Elements ']' token sequence.

I’m sure there’s a way to fix the conflict that I haven’t discovered yet, but it would be nice if Dhall’s grammar could be as straightforward to translate into an LALR(1) or LR(1) grammar as it was previously. I’ve experimented with a branch that only has the single top parse rule for type ascription and merges the annotation type into the list AST after parsing, but that would parse differently from your library.

Thanks for the nice language!

RFC: List append operator proposal

This is a request for comments on a proposal to add support for a list append operator

Right now there is no list append operator. The closest thing Dhall has to such an operator is ./Prelude/List/concat (which is defined in terms of List/build and List/fold). The original reason for this was to encourage efficient concatenation of lists since lists are stored under the hood as Vectors, and if you want to concatenate N vectors the most efficient way to do so is to concatenate them all at once (which is O(N) time complexity) instead of pair-wise (which is O(N^2) time complexity).

However, there are some times when all you want to do is to append two lists. For example, I ran into this issue when trying to translate the second Jsonnet example on this page (and that was the original motivation for this RFC), where I had to append two lists and awkwardly had to use something like ./Prelude/List/concat Text [x, y] when I would have preferred x ++ y or something similar.

The reason that Text provides a built-in ++ operator is that there is no similar efficiency concern. Text is implemented under the hood as a Builder which has O(1) time complexity for append.

There is also a separate and orthogonal issue, which is what to name this list operator if Dhall were to provide support for one. ++ is already taken for appending Text, even though it is also the most natural thing to name the operator to append two lists.

So I will structure this three sets of orthogonal proposals for:

  • How to support the list append operator
  • What to name the operator
  • How to efficiently support the operator

In each set of proposals, I will also indicate which one I prefer. I also invite people to submit proposals of their own if they can think of a better way to support this.

Proposal set (A) - How to support the list operator

Proposal (A0) - Do nothing

Maybe this isn't such a big deal and users can live with using either ./Prelude/List/concat or a newly added ./Prelude/List/append that is not used as an infix operator.

Proposal (A1) - Add support for user-defined infix operators

Allow users to define new operators using the syntax:

let (++) = ./Prelude/List/append SomeType
in  ...

... and add ./Prelude/List/append to the ./Prelude

Proposal (A2) - Add a built-in list concatenation operator

This would add a new ListAppend constructor to the core syntax tree. See Proposal set (B) which covers what to name this operator

My preference

I prefer proposal (A2).

I don't recommend proposal (A1) because a user-defined list operator cannot take advantage of type inference. The type of ./Prelude/List/append would be:

(a : Type)  List a  List a  List a

... so you would need to instantiate a to a specific type each time you bound ./Prelude/List/append to the operator name. A built-in operator could take advantage of type inference when type-checking to infer the type of the list being concatenated.

I also don't think it hurts to add a new list append operator to the language since it's very simple to implement and I'm pretty sure some users will want to use this operator. It also is associative and has an identity, so it passes the bare minimum criteria for being an operator in Dhall. So this is why I don't think we should do nothing as in Proposal (A0).

Proposal set (B) - Name of the append operator

If we go with proposal (A1) then we should recommend a naming convention for this operator and if we go with proposal (A2) then we have to pick a name.

Proposal (B0) - Reuse the operator (++)

This would add new type-checking logic to infer which (++) operator the user meant by the types of the arguments and provide a helpful error message if there is a type mismatch.

Proposal (B1) - Name list append (++) and rename text append to (**)

This would make it unambiguous which append the user intended, which would improve error messages

Proposal (B2) - Name the list append or text append operator something else

Some other ideas for what to name either operator to avoid a conflict:

  • (<|>) (which is a valid list concatenation operator in Haskell, too)
  • (+++)
  • (#)
  • (%)

My preference

I prefer proposal (B1). The rationale behind these two names is to open the door in the future to (++) being overloaded to work on both List/Optional and (**) working on Text/List Text/Optional Text/List (List Text)/...

Or more, generally, using a mix of Dhall and Haskell pseudo-code, this is what I envision

class Alternative f where
    zero :: f a
    (++) :: f a -> f a -> f a

instance Alternative (List a) where
    zero = [] : List a
    (++) = Data.List.(++)

instance Alternative (Optional a) where
    zero = [] : Optional a

    -- Same as `(<|>)` in Haskell
    ([] : Optional a) ++ [x] = [x]
    [x] ++ _ = [x]

class Monoid m where
    one :: m
    (**) :: m -> m -> m

instance Monoid Text where
    one = ""
    (**) = Data.Text.append

instance Monoid m => Monoid (List m) where
    one = pure one
    (**) = liftA2 (**)

instance Monoid m => Monoid (Optional m) where
    one = pure one
    (**) = liftA2 (**)

These would have the nice property that (**) and (++) behave like multiplication and addition (thus the operator names):

x ** (y ++ z) = (x ** y) ++ (x ** z)

zero ** x = zero

... and all the other semiring laws.

I'm not sure that I will ever implement these more general overloaded versions of (++) and (**) but I would still like to have some underlying consistency explaining why they are named the way they are.

The other reason I prefer distinct names is that Dhall tries to encourage the user to express their intent as much as possible in order to improve error messages. Distinct operator names would make it clearer which concatenation operator the user meant to use.

The downside of this is that it would break existing code since it's definitely not a backwards-compatible change. However, Dhall is still a pretty young language so I think it's okay to still make breaking changes at this point.

Proposal set (C) - Performance

This section will discuss how to represent Dhall Lists internally and how that effects the time complexity of the most performance-sensitive operations that Dhall currently supports. All of the time complexities are in terms of E, the total number of list elements.

This decision is not as important to get right now since we can always change it later without changing Dhall language. This only affects the Dhall API.

Proposal (C0) - Continue to use Vector to store Lists

  • (++) - O(E)
  • List/length - O(1)
  • List/indexed - O(E)
  • List/last - O(1)

Proposal (C1) - Use Data.Sequence.Seq to store Lists

Better time complexity than Vector for append, but possibly worse constant factors:

  • (++) - O(log E)
  • List/length - O(1)
  • List/indexed - O(E) (Using Data.Sequence.mapWithIndex)
  • List/last - O(1)

Proposal (C2) - Use VectorBuilder.Builder.Builder to store Lists

Most efficient for append, but worst in other respects:

  • (++) - O(1)
  • List/length - O(E) (Converting to a Vector first)
  • List/indexed - O(E) (Converting to a Vector intermediate)
  • List/last - O(E) (Converting to a Vector first)

Proposal (C3) - Use a wrapper around VectorBuilder.Builder.Builder

This takes advantage of the fact that Dhall doesn't support very many operations, so we can use a final encoding to efficiently cache their results. This takes advantage of Haskell's laziness to only compute what we actually need:

data List a = List
    { head :: Maybe a
    , last :: Maybe a
    , length :: !Natural
    , elements :: VectorBuilder.Builder.Builder a
    , reversed :: VectorBuilder.Builder.Builder a
    }

The main operation that this doesn't improve the efficiency of is List/indexed. However, it might be possible to add more efficient support for that with an upstream patch to vector-builder

My preference

I'm partial to both (C1) and (C3) but I slightly lean towards (C1) (using Seq) because it's a simple and versatile data structure, and it's an improvement over Vector from a time-complexity standpoint for append. Constant factors don't matter as much for Dhall since it's not intended to be a high performance language.

Conclusion

So to summarize, my proposal is to:

  • Rename the text append operator to (**)
  • Add a new list append operator named (++)
  • Use Seq instead of Vector internally to represent Dhall lists to improve the efficiency of append

Anybody who is interested in this can suggest any other proposals. If nobody objects to the above proposal or suggests any other alternatives then I'll implement this after a week has passed.

Document escaping of single quotes

I'm no sure if I'm just missing this in the Dhall.Tutorial, but I was a bit confused by the fact that strings in dhall cannot contain single quotes, as in:

{ foo : "bar'baz" }

which gives

(stdin):1:11: error: expected: "${",
    "''${", "\"", literal character
{foo= "bar'baz"} 

Is that even the intended behavior?

Type synonyms for Haskell defined types

I'd like to let the users write lambdas like:

λ(x : Big) -> ...

where Big is the type synonym for some big (many fields) record or union type. This record data type will be generated from the Haskell code. It would be very inconvenient to make the users write it manually.

Unfortunately I do not see the way to achieve this.

Some of the changes I've thought about:

  1. Adding new TypeDef constructor to Expr
  2. Passing Context.lookup to typeWith and overloading it to return the record instead of Big
  3. Having typeWith work on some kind of Context (Either Synonym Expr) data type.
  4. New FromContext Text PathType constructor allowing replacing the types during loadWith.
  5. Somethings obvious that I am missing? :)

Record Subtyping / Union Types?

Are there any plans to support a form of record subtyping? For example if <: is the subtyping relation that considers

record1 <: record2 iff record1 contains a subset of the attributes of record2

The advantage is that you can allow users to have multiple configurations in one file and/or let them specify their own custom additonal files.

To be more concrete, consider this example:

$ cat > configSchema1
{ consumerKey: Text, accessToken: Text, browser: Text, username: Text }
$ cat > configSchema2
{ username: Text, timeout: Natural }
$ cat > programConfig
{ consumerKey: "123",
  accessToken: "123",
  browser: "chromium",
  username: Text
}

It would be cool if the programConfig could be typed as either configSchema1 or configSchema2.

Additional thought, what about anonymous type level unions? e.g. configSchema1 \/ configSchema2, where \/ is the type level union type for records, s.t. the result would be

{ consumerKey: Text, accessToken: Text, browser: Text, username: Text, timeout: Natural }

Then you could write:

$ cat > typedConfig
./programConfig : ./configSchema1 \/ ./configSchema2

Oh and of course besides the union thing, subtyping would allow it to pass "larger" records into functions taking smaller ones:

let f = \(r: {foo: Text, bar: Natural}) -> r.foo
in f { foo = "123", bar = +1, baz = "foobar" } 

Which would currently fail because the record as an unexpected field baz

/= or !=

The title of the function is still /=, but all examples are !=.

Try to write a Dhall-to-CSS compiler

This was a great idea suggested by @ixmatus

You could actually integrate this with an existing Haskell CSS library by writing an Interpret instance for that library's CSS abstract syntax tree and then you would just load the tree from a Dhall configuration file and then render the tree to CSS

Improve escaping string `\` and `"` in dhall configuration file

I am converting 'shell mappings'; mappings from a name to a shell command.

As an example one mapping looks like so (in a bash file)

stats () {
    pepper --client=runner manage.status | jq '.return[0] | [.up , .up + .down | length] as $stats | {up, down, stats: "\($stats[0]) up of \($stats[1])"}'
}

I have a turtle script that executes: nix-shell ... --command 'stats'.

The idea is to replace such indirection with a direct call (in turtle):

 nix-shell ... --command "pepper --client=runner manage.status | jq '.return[0] | [.up , .up + .down | length] as \$stats | {up, down, stats: \"\(\$stats[0]) up of \(\$stats[1])\"}'"

And get the command string from a configuration file (read by dhall).

Unfortunately because of the default haskell escaping rules I have to come up with this:

{ stats = "pepper --client=runner manage.status | jq '.return[0] | [.up , .up + .down | length] as \\$stats | {up, down, stats: \\\"\\(\\$stats[0]) up of \\(\\$stats[1])\\\"}'"
}

A rather insane escaping mess ... I need to replace all \ with \\ and \" with \\\".

Is there a way to tell dhall to ignore all escape sequences (maybe with another string delimiter) ?

Any other advices would be welcomed. Thanks for your help.

PS: I believe this is actually a good use case for dhall because some of these 'mappings' require a set of arguments, some of them optional (managing those with shell functions is a pain)

Type annotation cannot refer bindings in scope

In "fixed" example from #9 different kind of error occurs to the user. The type checker cannot determine that {x : Integer, y : Integer} == Point.

$ ./dhall <<< "let Point = {x : Integer, y : Integer} in ({x = 42, y = -1} : Point)"

Use "dhall --explain" for detailed errors

Point : Type

Error: Expression doesn't match annotation

({x = 42, y = -1} : Point)

(stdin):1:43

Being able to declare type aliases and then use them in type annotations is especially useful when it comes to List or Optional type, because literals of such types always require type annotations. Duplicating type of large record does not seem practical.

Add Dhall to Stackage

This is just a reminder to myself to add dhall to Stackage after a few months of stability

Replace microlens by lens

I believe I have seen the same issue in one of your other packages.

Anyhow, because trifecta already uses lens, microlens and microlens-mtl forces users to pull in two extra dependencies.

Please, feel free to close this immediately if you feel it is not an issue.

[Question] Configurations with many optional fields

I'm looking at Dhall for the first time, and I like what I see so far.

When building systems the configuration for a component may have a small number of required fields, and a larger number of fields that have sensible defaults, but may be overriden as required. For an example, consider the terraform specfication for an AWS EC2 instance. There are 24 configurable fields, some of which must be provided, the others have useful defaults.

How would this be expressed in dhall? The straighforward translation would seem to to use the Optional builtin, ie:

$ cat configEc2
\(params : {
    ami : Text,
    availability_zone : Optional Text,
    placement_group : Optional Text,
    instance_type : Text
  }) -> "...build some structure out of the parameters, filling in the defaults..."

But the issue with this is that I have to provide all of the optional fields, along with their types, ie:

$ cat test3
./configEc2 {
  ami = "ami-12345",
  availability_zone = [] : Optional Text,
  placement_group = [] : Optional Text,
  instance_type = "t2.small"
}

With many optional values, this is verbose and tedious. It also doesn't allow optional values to be added without updating all of the call sites. Is there a means by which I only have to name the required fields, along with any options that I wish to override?

Dealing with Dhall.input error message

The default dhall error message is a bit hard to digest for user. Here is an example:

cicd: 
↳ /vagrant/config/shell 
/vagrant/config/shell:8:1: error: expected: "[",
    "\8704", "\955", "\\", "forall",
    "if", "let", "merge"
{ loginId      = "jdoe" 
^

I can easily catch "SomeException" to prefix the message. For instance:

→ result/bin/cicd prod facts
Fail to read configuration file
cicd: 
↳ /vagrant/config/shell 
/vagrant/config/shell:8:1: error: expected: "[",
    "\8704", "\955", "\\", "forall",
    "if", "let", "merge"
{ loginId      = "jdoe" 
^        

Except that ideally it would be nice to append the prefix after the name of the program. As the use case is probably quite common, it might be a bit unfair to ask the library user to always deal with the exception (for instance by wrapping the call to input with a try).

I have noticed as well that the error itself is not really helpful in this case because it actually happens in the next line :

{ loginId      = "jdoe"
, password  = 'secret"
}

Getting best mileage with `typeWith`?

I'm playing with Dhall (probably in ways it wasn't supposed to be used) and I'm having troubles with the typeWith function. What I would want is to have few opaque type name that can be referred to in my config. For most part this works fine, except that load does typechecking. This means that I cannot refer to my opaque types in anything that has been imported.

The above is probably by design, but it certainly makes typeWith less useful for the end user. Would a patch that enables passing type context into load be acceptable?

+ in front of Naturals is not at all natural

I nearly stumbled over the very first exercise because it was not at all clear to me that I need to annotate even naturals with +.

And the error message thrown is simply:

Error: Expression doesn't match annotation

./tmp/config : { age : Natural, name : Text }

I’m sure the explicit + has some deeper reasons, but do we need it for naturals? If yes, the tutorial should be very clear of this upfront.

[Question]: Explicitness about List type in a Dhall program ?

Hi,

While reading the tutorial, I don't quite understand why we need to be explicit about the List type in a Dhall program. In other word, why is this wrong:

>>> input auto "[True, False]" :: IO (Vector Bool)
[True,False]
// or
>>> input (vector bool) "[True, False]"

I understand this is a rule from

Every list must be followed by the type of the list. The type annotation is not optional and you will get an error if you omit the annotation

But there isn't any explanation about why it is so ;-)

Revisit precedence of let ... in and type annotation syntax or improve error message

The following example:

let Point = {x : Integer, y : Integer}
in {x = 42, y = -1} : Point

Gets parsed as:

(let Point = {x : Integer, y : Integer}
in {x = 42, y = -1}) : Point

Which leads to a pretty confusing error message, since one might expect that the type annotation will regard only expression {x = 42, y = -1} (like in e.g. Haskell).

$ ./dhall <<< "let Point = {x : Integer, y : Integer} in {x = 42, y = -1} : Point"

Use "dhall --explain" for detailed errors

Error: Unbound variable

Point

(stdin):1:62

Putting record and its type annotation in parentheses fixes this error, but introduces different one (see #10).

let Point = {x : Integer, y : Integer}
in ({x = 42, y = -1} : Point)

Bash backend

Given a record file :

{ foo = True
, baz = "hello"
, bar = 2
}

I would like to access bar using the dhall program alone (no haskell) inside a shell file.

I guess there is a simple trick but I don't see it ;-)

Add new conversion primitives

As mentioned in #47, we need the following new builtin functions in order to render numeric values:

  • Natural/show : Natural → Text
  • Integer/show : Integer → Text
  • Double/show : Double → Text
  • Natural/toInteger : Natural → Integer

The show functions should render in the exact same format that Dhall parses to source code. The purpose of the Natural/toInteger function is to help render Natural numbers without a leading + by converting them to Integers first

Add the ability to customize the pgrname error message prefix

Thanks to #25 the error message displays by dhall is much better.

But somehow I still believe it would be nice if I could optionally change the 'program name' line with a more custom (business related) one.

As an example, in my case, whenever an error occurs instead of "

cicd:
↳ /vagrant/config/shell
 
Error: Invalid input

I would display:

Fail to load configuration file: 
↳ /vagrant/config/shell 

Error: Invalid input

Cheers,

Further thoughts on the mandatory type annotation for lists

It might improve write-ability of lists if the user only had to specify the type for empty list literals.
I know that it’s in the syntax parser atm, but it might make sense to move it to the inference algorithm.

Another idea:

In practice, non-empty lists are probably used a lot more than normal lists?
So how about changing the literal to be a non-empty list by default? Lists could then be defined as non-empty + nil and the annotation problem is gone (I hope). It also mirrors the “Monoid is a semigroup with zero element” structure better.
In practice users would probably use Maybe [] for the “may be empty” case.
Actually I really like that idea.

Multiline strings and string interpolation

I am currently writing a blog system with Haskell and I store the posts as Haskell values:

module Entries.E170426 where

import Imports
import Types

entry = Entry
  { entryTitle = "Hello World!"
  , entryCreated = fromGregorian 2017 04 26
  , entryUpdated = fromGregorian 2017 04 26
  , entryKeywords = [Blogging]
  , entryCategory = []
  , entryLanguage = English
  , entryComments = Github
  , entryAbstract = [md|

This is my first blog post.

|]
  , entryContent = [md|
... 
|]}

The benefit of this is, that I can add extra information like keywords and rely on the type checker to check that they are valid (for example using the keyword "Bloging" would be a compile-time error).
The reason I don't use pandoc filters for this is that I want use arbitrary Haskell data types in the quasiquote via an escaping mechanism allowing me to implement microformats and schema.org markup with ease. The problem is that I will have to use haskell-src-exts as an additional dependency for that.

So I am currently looking at dhall as an alternative for that use case. I would write a blog entry in a dhall file as an Entry data type and import the files into Haskell. However, I need multi-line strings (like in the md quasi-quote above) and string interpolation in the form of

let entry = """
some text..
@{ show $ Rating
  { name = "Haskell"
  , rating = 5
  }
}
some text..
""" in entry

As this is probably not an uncommon requirement for a configuration language, I wanted to ask whether it would make sense to add these features.

Dhall for HTML templating

This shouldn't be read as a feature request. It's more like a idea that I've been musing over and just wanted to share, since future iterations of dhall or a dhall-like language may consider this as a use case.

I think that using dhall for static site HTML generation would be useful. In the simplest case, imagine the kind of site where you have a common header and footer on every page, and you just want to stick in a macro that's going to expand. A more interesting scenario would be where you want to generate something like the list group badges example from the bootstrap css docs:

<ul class="list-group">
  <li class="list-group-item">
    <span class="badge">14</span>
    Maria
  </li>
  <li class="list-group-item">
    <span class="badge">2</span>
    Jordan
  </li>
  <li class="list-group-item">
    <span class="badge">1</span>
    Pranav
  </li>
</ul>

There's a ton of redundancy in this HTML. Typically, I find myself reaching for something like hakyll or heist to address these needs, but it would be nice to use the plain old System F normalization that dhall provides. What if I could write something like this:

File: Person.dhall
{ name: Text, upvotes: Natural }

File: people.dhall
[ {name = "Maria", upvotes = +14}
, {name = "Jordan", upvotes = +2}
, {name = "Pranav", upvotes = +1}
] : List ./Person.dhall

File: single-item.html
<lambda>
  <argument name="person" type="./Person.dhall"/>
  <li class="list-group-item">
    <span class="badge"><var expression="person.age" type="Natural"/></span>
    <var expression="person.name" type="Text"/>
  </li>
</lambda>

File all-items.html
<ul class="list-group">
  <apply function="Html/concatMap">
    <pass-argument expression="./single-item.html" />
    <pass-argument expression="./people.dhall" />
  </apply>
</ul>

This is not exactly what I would want the html interface to dhall to look like, but this does accurately convey the kind of things I would like to be able to do.

While it is currently possible to write something that parses xml into the dhall syntax tree, dhall's syntax tree does not provide a way to represent an HTML DOM. Doing this would require adding (at the least):

  • Some kind of XmlNode type
  • operators for casting Int, Natural, and Text to XmlNode
  • various primitives for combining XmlNodes

This has to be added to the syntax tree itself because dhall does not allow users to define recursive data types. I have a branch where I've tried to do this, but I gave up on it for reasons I cannot remember. I think I ran into difficulty with having it be possible to represent the same thing two different ways.

If dhall or some language like it allowed users to define recursive data types (or even just provided a fixed-point combinator, not sure if this is actually sound though), I think that most of the difficulty would be solved. Escaping text when converting it to HTML would still probably need built-in support though.

Again, this was all meant as a use-case to think about. While it would be cool to be able to express this, especially if it's compatible with standard dhall, I still think dhall's pretty cool even without it.

Text editor integration?

I'm wondering your thoughts on what widespread Dhall usage might look like.

  • Should Dhall files have a common file extension? I've just been using .txt; I suppose one might reasonably consider .hs or .dhall.
  • How might syntax highlighting in text editors work? Should any Haskell syntax highlighter suffice for Dhall, or would we be better off creating Dhall plugins for various editors?

HTTP Authentication

It would be useful to be able to import Dhall expressions from URLs that require authentication such as retrieving a file from a private GitHub repository.

In the GitHub file case, it would be sufficient to allow one to specify Authorization and Accept headers to access a file using https://api.github.com.

Dhall and Turtle

If I am not mistaken, dhall and turtle v1.3.0 won't work together because given the following stack.yaml :

resolver: lts-7.11
extra-deps:
- turtle-1.3.0
- optparse-applicative-0.13.0.0
- dhall-1.0.1
→ stack install

While constructing the build plan, the following exceptions were encountered:

In the dependencies for optparse-generic-1.1.1:
    optparse-applicative-0.13.0.0 must match >=0.11.0 && <0.13 (latest applicable is 0.12.1.0)
needed due to cicd-shell-0.1.0 -> optparse-generic-1.1.1

Plan construction failed.

Nicer errors for “type synonyms”

It’s simple to give a “scheme” to check a config file against:

→ echo '{foo : Integer, bar : Double}' > ./ConfigScheme
→ echo '{foo = 3, bar = 4.0} : ./ConfigScheme' | dhall
{ bar : Double, foo : Integer }

{ bar = 4.0, foo = 3 }

where ConfigScheme is provided by the creator of the config file format and the values are provided by the user.

If the user now inputs a mistyped value, dhall responds with the following error by default:

→ echo '{foo = 3, bar = 4} : ./MyType' | dhall

Use "dhall --explain" for detailed errors

Error: Expression doesn't match annotation

{foo = 3, bar = 4} : ./MyType

(stdin):1:1

I’d expect something along the lines of:

Error: Expression doesn't match annotation

{foo = 3, bar = 4} : ./MyType
bar is Integer
need   Double

(stdin):1:1

That’s interesting, because type aliases might be a nice addon? But maybe they aren’t needed since we can already use named files for that. Maybe the dhall type checker should treat files of kind Type as type aliases.

defaultInterpretOptions is not inherited from parent record

Given this code:

auto :: (GenericInterpret (Rep a), Generic a) => Type a
auto = deriveAuto
  ( defaultInterpretOptions { fieldModifier = Data.Text.Lazy.dropWhile (== '_') })

data GitRepo
  = GitRepo
  { _host :: LText
  , _repo :: LText
  } deriving (Generic, Show)

data BoxConfig
  = BoxConfig
  { _userName        :: LText
  , _dotfilesRepo    :: GitRepo
  } deriving (Generic, Show)

makeLenses ''BoxConfig
makeLenses ''GitRepo

instance Interpret GitRepo
instance Interpret BoxConfig

boxConfig :: IO BoxConfig
boxConfig = Dhall.input auto "/vagrant/config/box"

It will force me to have this config file:

{ userName       = "John Doe"
, dotfilesRepo   = { _host = "github", _repo = "jdoe/dotfiles.git"}
}

but I wish to have

{ userName       = "John Doe"
, dotfilesRepo   = { host = "github", repo = "jdoe/dotfiles.git"}
}

Use "in" as a field name (despite its keyword status)

Is there any way to produce a record with a field name "in"? (I fear not, since it's a keyword for let expressions.) I was playing with the idea of using Dhall to produce a YAML file (with dhall-to-yaml) for use with Swagger (http://swagger.io), which as part of defining parameters that an API endpoint can accept, uses an object key "in" to specify where a parameter can occur.

For example,

{ swagger = "2.0"
, parameters = {
    foo = {
      name = "foo"
    , in = "path"
    , type = "string"
    }
  }
  ...
}

to produce

    swagger: "2.0"
    parameters:
        foo:
            name: "foo"
            in: "path"
            type: "string" 
    ...

Try to write a Dhall-to-Nix compiler

Nix lets you import Nix expressions from derivation outputs (although I believe this is still considered experimental), so you could create a Nix derivation that reads in a Dhall configuration file and outputs a Nix expression which could then be referenced by other Nix expressions

This would in turn let you carve out statically typed subsets of Nix projects using Dhall

cc: @domenkozar, who might also be interested in this

Representing implicit parameters

Consider configuration file having many parameters e.g sshd.conf. Most of the parameters have default values. Another example can be HTML: the nodes have lots of attributes, none of them required.

How to encode such configuration files in Dhall?

Why [] and [] for the type & value?

You have a hard restriction that we must specify a type for the list value, because it’s not clear whether it’s the type or the empty list.

This leads to … interesting config files in practice.

Idea:

change the value [] to the symbol empty.

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.