lambdaisland / deep-diff2 Goto Github PK
View Code? Open in Web Editor NEWDeep diff Clojure data structures and pretty print the result
License: Eclipse Public License 1.0
Deep diff Clojure data structures and pretty print the result
License: Eclipse Public License 1.0
Currently, colors are hard-coded, but there are various situations where you might want to set different colors:
In addition to general usefulness, parameterizing color schemes may address #14 and would be required for lambdaisland/kaocha#403.
See comments on #12 , apparently we can make puget fall back to the system printer. I think it makes sense to do that unless specified otherwise.
Sadly this would constiute a breaking change, but without it custom types don't print in any meaningful way, and if people have registered their own print handlers they will still take precedence, so I think we can still consider this.
Something the generative tests caught, not sure this is a big issue but something to look into.
From property-based tests running on CI:
[:fail] expected: {:result true}. actual: {:shrunk {:total-nodes-visited 122, :depth 17, :pass? false, :result false, :result-data nil, :time-shrinking-ms 43, :smallest [{{##NaN 0} 0} {}]}, :failed-after-ms 266, :num-tests 100, :seed 1656705885608, :fail [{{S8.di092E8-!ubS -31168388659169371747161610336919826167194645301356227082938375168857766470903802989439005434804565N, ##NaN H5U} #:!9BIU60.-{I:T 5/9}} {}], :result false, :result-data nil, :failing-size 99, :pass? false, :test-var "round-trip-diff"}
FAIL in lambdaisland.deep-diff2.diff-test/round-trip-diff (diff_test.cljc:211)
expected: {:result true}
actual: {:shrunk {:total-nodes-visited 122, :depth 17, :pass? false, :result false, :result-data nil, :time-shrinking-ms 43, :smallest [{{##NaN 0} 0} {}]}, :failed-after-ms 266, :num-tests 100, :seed 1656705885608, :fail [{{S8.di092E8-!ubS -31168388659169371747161610336919826167194645301356227082938375168857766470903802989439005434804565N, ##NaN H5U} #:!9BIU60.-{I:T 5/9}} {}], :result false, :result-data nil, :failing-size 99, :pass? false, :test-var "round-trip-diff"}
Looks like the case to potentially add to our unit-tests is [{{##NaN 0} 0} {}]
(defrecord ARecord [])
(diff/diff (map->ARecord {}) (map->ARecord {:a 1}))
; Exception: java.lang.ClassCastException: lambdaisland.deep_diff.diff_test.ARecord cannot be cast to clojure.lang.IFn at clojure.core$juxt$fn__5544.invoke (core.clj:2586)
; ...
; lambdaisland.deep_diff.diff$diff_map.invokeStatic (diff.clj:125)
; lambdaisland.deep_diff.diff$diff_map.invoke (diff.clj:106)
; lambdaisland.deep_diff.diff$eval2918$fn__2919.invoke (diff.clj:162)
; lambdaisland.deep_diff.diff$eval2796$fn__2797$G__2787__2804.invoke (diff.clj:11)
; lambdaisland.deep_diff.diff$diff.invokeStatic (diff.clj:136)
; lambdaisland.deep_diff.diff$diff.invoke (diff.clj:134)
; lambdaisland.deep_diff.diff_test$fn__16191$fn__16204.invoke (diff_test.clj:107)
; lambdaisland.deep_diff.diff_test$fn__16191.invokeStatic (diff_test.clj:33)
; lambdaisland.deep_diff.diff_test/fn (diff_test.clj:19)
ClojureScript support should be fairly easy once all upstream dependencies have ClojureScript support.
More info in this thread: pjstadig/humane-test-output#34
Thanks for providing deep-diff, it's a super usable tool.
We do think we've found a bug in version 2.0.108 though. We we're able to minimize the test case to this:
(require
'[lambdaisland.deep-diff2 :as ddiff])
(def actual [nil])
(def expected [{}])
;; works
(ddiff/diff actual expected)
;; does not terminate
(ddiff/diff expected actual)
So the last line seems to never terminate. Any ideas what might cause this?
Hi :) I tracked down a weird hanging issue to deep-diff/diff
. A minimal repro case:
❯ clj -Sdeps '{:deps {lambdaisland/deep-diff {:mvn/version "0.0-25"}}}'
Clojure 1.10.0
user=> (require '[lambdaisland.deep-diff :as ddiff])
user=> user=> (ddiff/diff {:foo :bar} {:foo :bar})
{:foo :bar}
user=> (ddiff/diff {:foo :bar} {nil :wat :foo :bar})
{:foo :bar, #lambdaisland.deep_diff.diff.Insertion{:+ nil} :wat}
user=> (ddiff/diff {nil :wat :foo :bar} {:foo :bar})
;;;; Here we just hang; probably falling into an infinite loop/recur?
Thanks for the awesome tool! We are currently using it in an internal web-based integration testing tool. It's working great right up until trying to display a pretty printed version of diffs.
It seems the pretty printer messes up the diff when compiled with advanced optimizations.
Here is a shell session demonstrating the problem:
❯ tree
.
├── deps.edn
└── src
└── ddiff_bug
└── core.cljs
3 directories, 2 files
❯ bat deps.edn
1 {:paths ["src"]
2 :deps { org.clojure/clojurescript {:mvn/version "1.11.132"}
3 lambdaisland/deep-diff2 {:mvn/version "2.11.216"}}}
❯ bat src/ddiff_bug/core.cljs
1 (ns ddiff-bug.core
2 (:require [lambdaisland.deep-diff2 :as ddiff]))
3
4
5 (enable-console-print!)
6
7 (defn diff-view
8 [a b]
9 (let [diff (ddiff/diff a b)
10 diffstr (with-out-str (ddiff/pretty-print diff))]
11 (println "\nDocument A")
12 (println a)
13 (println "\nDocument B")
14 (println b)
15 (println "\nDiff")
16 (println diff)
17 (println "\nPretty printed diff")
18 (println diffstr)))
19
20 (defn -main
21 [& args]
22 (diff-view {"Hi" {:foo 424128, :bar 22140, :baz 243000}}
23 {"Ho" {:foo 424128, :bar 22140, :baz 243000}}))
24
25
26 (-main)
27
❯ clj -M --main cljs.main --target node --output-to main-dev.js --optimizations none --compile ddiff-bug.core
❯ clj -M --main cljs.main --target node --output-to main-prod.js --optimizations advanced --compile ddiff-bug.core
❯ node main-dev.js
Document A
{Hi {:foo 424128, :bar 22140, :baz 243000}}
Document B
{Ho {:foo 424128, :bar 22140, :baz 243000}}
Diff
{#lambdaisland.deep-diff2.diff-impl.Deletion{:- Hi} {:foo 424128, :bar 22140, :baz 243000}, #lambdaisland.deep-diff2.diff-impl.Insertion{:+ Ho} {:foo 424128, :bar 22140, :baz 243000}}
Pretty printed diff
{+"Ho" {:bar 22140, :baz 243000, :foo 424128}, -"Hi" {:bar 22140, :baz 243000, :foo 424128}}
❯ node main-prod.js
Document A
{Hi {:foo 424128, :bar 22140, :baz 243000}}
Document B
{Ho {:foo 424128, :bar 22140, :baz 243000}}
Diff
{#lambdaisland.deep-diff2.diff-impl.Deletion{:- Hi} {:foo 424128, :bar 22140, :baz 243000}, #lambdaisland.deep-diff2.diff-impl.Insertion{:+ Ho} {:foo 424128, :bar 22140, :baz 243000}}
Pretty printed diff
{#Xo {:+ "Ho"} {:bar 22140, :baz 243000, :foo 424128},
#Wo {:- "Hi"} {:bar 22140, :baz 243000, :foo 424128}}
It seems minimized names (as seen in the Xo
and Wo
tags here) are confusing the printer. The two tags seen here are minimized versions of $lambdaisland$deep_diff2$diff_impl$Insertion$$
and $lambdaisland$deep_diff2$diff_impl$Deletion$$
In certain cases the diff shows higher up in the hierarchy then expected. As an example:
(require '[lambdaisland.deep-diff2 :as diff])
(def d1 #{{:foo 1M} {:bar 2}})
(def d2 #{{:foo 1} {:bar 2}})
(diff/pretty-print (diff/diff d1 d2))
;; #{+{:foo 1} -{:foo 1M} {:bar 2}}
(def d1 #{{:foo 1M}})
(def d2 #{{:foo 1}})
(diff/pretty-print (diff/diff d1 d2))
;; #{{:foo -1M +1}}
I would have expected the diff to appear in the first case the same as in the second case.
I sometimes need to write tests for functions that output strings, in particular multi-line strings. Right now deep-diff2 will just say -<expected-string> +<actual-string>
, which isn't very useful for longer strings and strings containing newlines.
Right now I try to work around this by splitting the strings on newlines and letting deep-diff2 diff the arrays, which at least will highlight the lines with changes, but it's not great.
I think it'd be useful to print a string-based diff, like git-diff would, if both expressions for a mismatch are string.
For no newlines they could still be printed in place, but for newlines it likely would make sense to introduce a linebreak and then show the diff without worrying about indentation.
I found deep-diff2.printer-impl/print-mismatch
, and (and (string? (:- expr)) (string? (:+ expr)))
seems like an easy enough condition there, so I'm playing around with it.
Would you consider this for deep-diff2, or do you see problems with the approach? Perhaps it could be an option to not break any existing consumers.
Eg. https://github.com/clj-commons/ordered provides ordered collections, and the idea really is that there's no reshuffling of especially KV pairs in maps to make it even easier to spot differences between what was provided and what the diff was.
Cljdoc analysis is failing for the much loved deep-diff2
(I'm going to assume you care because you have a cljdoc badge on your readme 🙂)
Analysis job shows (logs aren't saved forever so I'll paste too):
2022-05-16 06:36:27,679 ERROR cljdoc-analyzer.runner - STDERR
{:clojure.main/message
"Execution error (FileNotFoundException) at lambdaisland.deep-diff2.diff-test/eval4071$loading (diff_test.cljc:1).\nCould not locate clojure/test/check__init.class, clojure/test/check.clj or clojure/test/check.cljc on classpath.\n",
:clojure.main/triage
{:clojure.error/class java.io.FileNotFoundException,
:clojure.error/line 1,
:clojure.error/cause
"Could not locate clojure/test/check__init.class, clojure/test/check.clj or clojure/test/check.cljc on classpath.",
:clojure.error/symbol
lambdaisland.deep-diff2.diff-test/eval4071$loading,
:clojure.error/source "diff_test.cljc",
:clojure.error/phase :execution},
:clojure.main/trace
{:via
[{:type clojure.lang.ExceptionInfo,
:message
"Could not generate Clojure documentation for lambdaisland.deep-diff2.diff-test",
:data {},
:at
[cljdoc_analyzer.metagetta.utils$default_exception_handler
invokeStatic
"utils.clj"
118]}
{:type clojure.lang.Compiler$CompilerException,
:message
"Syntax error compiling at (lambdaisland/deep_diff2/diff_test.cljc:1:1).",
:data
{:clojure.error/phase :compile-syntax-check,
:clojure.error/line 1,
:clojure.error/column 1,
:clojure.error/source "lambdaisland/deep_diff2/diff_test.cljc"},
:at [clojure.lang.Compiler load "Compiler.java" 7648]}
{:type java.io.FileNotFoundException,
:message
"Could not locate clojure/test/check__init.class, clojure/test/check.clj or clojure/test/check.cljc on classpath.",
:at [clojure.lang.RT load "RT.java" 462]}],
:trace
[[clojure.lang.RT load "RT.java" 462]
[clojure.lang.RT load "RT.java" 424]
[clojure.core$load$fn__6839 invoke "core.clj" 6126]
[clojure.core$load invokeStatic "core.clj" 6125]
[clojure.core$load doInvoke "core.clj" 6109]
[clojure.lang.RestFn invoke "RestFn.java" 408]
[clojure.core$load_one invokeStatic "core.clj" 5908]
[clojure.core$load_one invoke "core.clj" 5903]
[clojure.core$load_lib$fn__6780 invoke "core.clj" 5948]
[clojure.core$load_lib invokeStatic "core.clj" 5947]
[clojure.core$load_lib doInvoke "core.clj" 5928]
[clojure.lang.RestFn applyTo "RestFn.java" 142]
[clojure.core$apply invokeStatic "core.clj" 667]
[clojure.core$load_libs invokeStatic "core.clj" 5985]
[clojure.core$load_libs doInvoke "core.clj" 5969]
[clojure.lang.RestFn applyTo "RestFn.java" 137]
[clojure.core$apply invokeStatic "core.clj" 667]
[clojure.core$require invokeStatic "core.clj" 6007]
[clojure.core$require doInvoke "core.clj" 6007]
[clojure.lang.RestFn invoke "RestFn.java" 512]
[lambdaisland.deep_diff2.diff_test$eval4071$loading__6721__auto____4072
invoke
"diff_test.cljc"
1]
[lambdaisland.deep_diff2.diff_test$eval4071
invokeStatic
"diff_test.cljc"
1]
[lambdaisland.deep_diff2.diff_test$eval4071
invoke
"diff_test.cljc"
1]
[clojure.lang.Compiler eval "Compiler.java" 7177]
[clojure.lang.Compiler eval "Compiler.java" 7166]
[clojure.lang.Compiler load "Compiler.java" 7636]
[clojure.lang.RT loadResourceScript "RT.java" 381]
[clojure.lang.RT loadResourceScript "RT.java" 372]
[clojure.lang.RT load "RT.java" 459]
[clojure.lang.RT load "RT.java" 424]
[clojure.core$load$fn__6839 invoke "core.clj" 6126]
[clojure.core$load invokeStatic "core.clj" 6125]
[clojure.core$load doInvoke "core.clj" 6109]
[clojure.lang.RestFn invoke "RestFn.java" 408]
[clojure.core$load_one invokeStatic "core.clj" 5908]
[clojure.core$load_one invoke "core.clj" 5903]
[clojure.core$load_lib$fn__6780 invoke "core.clj" 5948]
[clojure.core$load_lib invokeStatic "core.clj" 5947]
[clojure.core$load_lib doInvoke "core.clj" 5928]
[clojure.lang.RestFn applyTo "RestFn.java" 142]
[clojure.core$apply invokeStatic "core.clj" 667]
[clojure.core$load_libs invokeStatic "core.clj" 5985]
[clojure.core$load_libs doInvoke "core.clj" 5969]
[clojure.lang.RestFn applyTo "RestFn.java" 137]
[clojure.core$apply invokeStatic "core.clj" 667]
[clojure.core$require invokeStatic "core.clj" 6007]
[clojure.core$require doInvoke "core.clj" 6007]
[clojure.lang.RestFn invoke "RestFn.java" 408]
[cljdoc_analyzer.metagetta.clojure$read_ns$fn__1615
invoke
"clojure.clj"
101]
[cljdoc_analyzer.metagetta.clojure$read_ns
invokeStatic
"clojure.clj"
100]
[cljdoc_analyzer.metagetta.clojure$read_ns invoke "clojure.clj" 95]
[cljdoc_analyzer.metagetta.clojure$read_namespaces$fn__1628
invoke
"clojure.clj"
167]
[clojure.core$map$fn__5866 invoke "core.clj" 2755]
[clojure.lang.LazySeq sval "LazySeq.java" 42]
[clojure.lang.LazySeq seq "LazySeq.java" 51]
[clojure.lang.Cons next "Cons.java" 39]
[clojure.lang.RT boundedLength "RT.java" 1792]
[clojure.lang.RestFn applyTo "RestFn.java" 130]
[clojure.core$apply invokeStatic "core.clj" 665]
[clojure.core$mapcat invokeStatic "core.clj" 2783]
[clojure.core$mapcat doInvoke "core.clj" 2783]
[clojure.lang.RestFn invoke "RestFn.java" 423]
[cljdoc_analyzer.metagetta.clojure$read_namespaces
invokeStatic
"clojure.clj"
167]
[cljdoc_analyzer.metagetta.clojure$read_namespaces
invoke
"clojure.clj"
131]
[cljdoc_analyzer.metagetta.main$read_namespaces
invokeStatic
"main.clj"
72]
[cljdoc_analyzer.metagetta.main$read_namespaces
invoke
"main.clj"
68]
[cljdoc_analyzer.metagetta.main$get_metadata$fn__1786
invoke
"main.clj"
103]
[clojure.core$mapv$fn__8445 invoke "core.clj" 6912]
[clojure.lang.ArraySeq reduce "ArraySeq.java" 111]
[clojure.core$reduce invokeStatic "core.clj" 6827]
[clojure.core$mapv invokeStatic "core.clj" 6903]
[clojure.core$mapv invoke "core.clj" 6903]
[cljdoc_analyzer.metagetta.main$get_metadata
invokeStatic
"main.clj"
100]
[cljdoc_analyzer.metagetta.main$get_metadata invoke "main.clj" 86]
[cljdoc_analyzer.metagetta.main$_main invokeStatic "main.clj" 134]
[cljdoc_analyzer.metagetta.main$_main invoke "main.clj" 109]
[clojure.lang.AFn applyToHelper "AFn.java" 154]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.lang.Var applyTo "Var.java" 705]
[clojure.core$apply invokeStatic "core.clj" 665]
[clojure.main$main_opt invokeStatic "main.clj" 514]
[clojure.main$main_opt invoke "main.clj" 510]
[clojure.main$main invokeStatic "main.clj" 664]
[clojure.main$main doInvoke "main.clj" 616]
[clojure.lang.RestFn applyTo "RestFn.java" 137]
[clojure.lang.Var applyTo "Var.java" 705]
[clojure.main main "main.java" 40]],
:cause
"Could not locate clojure/test/check__init.class, clojure/test/check.clj or clojure/test/check.cljc on classpath."}}
Execution error (FileNotFoundException) at lambdaisland.deep-diff2.diff-test/eval4071$loading (diff_test.cljc:1).
Could not locate clojure/test/check__init.class, clojure/test/check.clj or clojure/test/check.cljc on classpath.
If I list the jar contents:
$ jar tf ~/.m2/repository/lambdaisland/deep-diff2/2.2.124/deep-diff2-2.2.124.jar
META-INF/MANIFEST.MF
META-INF/
lambdaisland/
lambdaisland/deep_diff2/
lambdaisland/deep_diff2/puget/
lambdaisland/deep_diff2/puget/color/
META-INF/maven/
META-INF/maven/lambdaisland/
META-INF/maven/lambdaisland/deep-diff2/
lambdaisland/deep_diff2_test.cljc
lambdaisland/deep_diff2/printer_impl.cljc
lambdaisland/deep_diff2/printer_test.cljc
lambdaisland/deep_diff2/diff_impl.cljc
lambdaisland/deep_diff2/puget_test.cljc
lambdaisland/deep_diff2/puget/dispatch.cljc
lambdaisland/deep_diff2/puget/color/ansi.cljc
lambdaisland/deep_diff2/puget/color/html.cljc
lambdaisland/deep_diff2/puget/printer.cljc
lambdaisland/deep_diff2/puget/color.cljc
lambdaisland/deep_diff2/diff_test.cljc
lambdaisland/deep_diff2.cljc
META-INF/maven/lambdaisland/deep-diff2/pom.xml
META-INF/maven/lambdaisland/deep-diff2/pom.properties
we can see all test sources are included (ex. deep_diff2_test.cljc
) - most likely not on purpose.
When cljdoc tries to analyze the jar, the test libs the test sources depend on aren’t included as deps for the jar therefore they won’t be found and… then analysis fails.
I'm not sure how the release jar is built, but the pom.xml
currently includes the test dir which might be the causing those test sources to be included.
We currently handle sets with the same logic that we use for sequential collection, but sets have no ordering, leading to issues. For instance, even when comparing a set with itself ddiff may imagine there are differences.
(let [s #{false 5}]
(ddiff/diff s s))
;; => #{{:- 5} false {:+ 5}}
I think we want to add a separate diff-set
function, which loops over the keys of set A, marking any element that's absent in set B as a deletion, and then runs over any remaining elemnts in set B, marking them as additions. We don't mark any as replacements.
It shouldn't matter in which order I add keys to a hashmap:
(ddiff/diff {:name "Alyysa P Hacker" :age 40} {:age 40 :name "Alyssa P Hacker"})
Expected
{:name #lambdaisland.deep_diff2.diff_impl.Mismatch{:- "Alyysa P Hacker", :+ "Alyssa P Hacker"}, :age 40}
Actual
The :age
key is removed and re-added:
{#lambdaisland.deep_diff2.diff_impl.Insertion{:+ :age} 40, :name #lambdaisland.deep_diff2.diff_impl.Mismatch{:- "Alyysa P Hacker", :+ "Alyssa P Hacker"}, #lambdaisland.deep_diff2.diff_impl.Deletion{:- :age} 40}
Similar to #3, but applies to records with deletions. I believe this applies to both deep-diff
and deep-diff2
.
This arises from invoking the record as a function in diff-map
which works for maps, but not records since they don't automatically implement IFn
.
I can put together a minimal repro and a PR in the next couple days.
lambdaisland.deep-diff.diff> (defrecord MyRecord [])
lambdaisland.deep_diff.diff.MyRecord
lambdaisland.deep-diff.diff> (diff (map->MyRecord {:foo "bar"}) (map->MyRecord {}))
Execution error (ClassCastException) at lambdaisland.deep-diff.diff/diff-map$fn (diff.clj:116).
class lambdaisland.deep_diff.diff.MyRecord cannot be cast to class clojure.lang.IFn (lambdaisland.deep_diff.diff.MyRecord is in unnamed module of loader clojure.lang.DynamicClassLoader @11f4ce76; clojure.lang.IFn is in unnamed module of loader 'app')
Hi there, I noticed that UUIDs and java.util.Date
s print as objects rather than as their tagged literal form.
(ddiff/pretty-print (ddiff/diff #inst "2019-04-01T14:38:55.998-00:00"
#inst "2019-04-01T14:39:11.732-00:00"))
prints as
-#<java.util.Date@5c6429fc Mon Apr 01 10:38:55 EDT 2019>
+#<java.util.Date@69ad72db Mon Apr 01 10:39:11 EDT 2019>
and
(ddiff/pretty-print (ddiff/diff #uuid "70fa1988-a1d0-42f3-9c81-8d60ac3e999a"
#uuid "0f2386f7-0587-46c7-8211-b3919f07fca8"))
prints as
-#<java.util.UUID@68e1706a 70fa1988-a1d0-42f3-9c81-8d60ac3e999a>
+#<java.util.UUID@5a4ce851 0f2386f7-0587-46c7-8211-b3919f07fca8>
would you be interested in a patch that includes printers for these?
A follow-up to #10, it would be great to also print the newer java.time.*
classes as #inst
.
Thanks so much for the kaocha, I am really enjoying using it!
Some of my integration tests include the comparison of largish data structures. It can take my eyeballs a while to find the diff between expected and actual in these data structures.
I do sometimes use the search feature in my terminal to search for + and - and that can help and I was thinking focusing only on the differences, as #13 suggests, would help.
But I am also thinking perhaps a different choice of colors might help. I like the choice of colors chosen for differences, but I find that the red can take a while to spot because it is already used in non-diff syntax highlighting.
My initial thoughts are:
I am happy to give some color schemes a whirl for review, then a PR with changes, if there is interest in this idea.
As part of porting deep-diff to clojurescript, we ported Puget to ClojureScript. Sadly this version is not compatible with upstream puget, since we can't do type dispatching in the same way. This also means that people need to explicitly add print handlers for custom types (although apparently there's an option to fall back to the regular printer, we should enable that).
Having puget available in ClojureScript for rendering of cljs data structures is very useful, and we should pull that out into its own lib/artifact.
How to skip printing values same in both structures? I am sorry, I am overwhelmed by fipp and puget and haven't managed to understand how to only print +/-/mismatches. Is there a way to override print-other
to be a noop or something? Sorry and thank you!
on:
[lambdaisland/deep-diff "0.0-25"]
some:
Reflection warning, lambdaisland/deep_diff/diff.clj:103:15 - reference to field getName can't be resolved.
Reflection warning, lambdaisland/deep_diff/diff.clj:146:34 - reference to field getClass can't be resolved.
Reflection warning, lambdaisland/deep_diff/diff.clj:146:24 - reference to field isArray can't be resolved.
Reflection warning, lambdaisland/deep_diff/printer.clj:80:27 - reference to field getName can't be resolved.
this is a test
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.