Giter Site home page Giter Site logo

react's Introduction

React โ€” Declarative events and signals for OCaml

Release %%VERSION%%

React is an OCaml module for functional reactive programming (FRP). It provides support to program with time varying values : declarative events and signals. React doesn't define any primitive event or signal, it lets the client chooses the concrete timeline.

React is made of a single, independent, module and distributed under the ISC license.

Homepage: http://erratique.ch/software/react

Installation

React can be installed with opam:

opam install react

If you don't use opam consult the opam file for build instructions.

Documentation

The documentation and API reference is automatically generated by from the source interfaces. It can be consulted online or via odig doc react.

Sample programs

If you installed React with opam sample programs are located in the directory opam var react:doc.

In the distribution sample programs are located in the test directory of the distribution. They can be built with:

ocamlbuild -use-ocamlfind test/tests.otarget

The resulting binaries are in _build/test.

  • test.native tests the library, nothing should fail.
  • clock.native is a command line program using ANSI escape sequences and the Unix module to print the current local time.
  • breakout.native is a command line program using ANSI escape sequences and the Unix module to implement a simple breakout game.

react's People

Contributors

dbuenzli avatar

Stargazers

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

react's Issues

Project Name

Considering React (JS / UI framework/library) became quite wide spread. While OCaml's FRP React exists for quite some time and has not gained much popularity. And this trend probably won't be reversed. Just as Jbuilder tool name was confusing to most people (because of Java's much more widespread tool), changing name to Dune (a different unrelated name) was nice. Would be nice if React (OCaml's FRP) had it's name changed to something more descriptive and less confusing to newcomers.

Triggering send when an event occurs without calling from within an update step

I would like to do something like this

open React

let x1, send_x1 = E.create ()
let x2, send_x2 = E.create ()
let x3, send_x3 = E.create ()
let y1, send_y1 = E.create ()
let y2, send_y2 = E.create ()
let y3, send_y3 = E.create ()

let connect x send_y = E.map (fun () -> send_y ()) x

let (_ : unit event) = connect x1 send_y1
let (_ : unit event) = connect x1 send_y2
let (_ : unit event) = connect x1 send_y3
let (_ : unit event) = connect x2 send_y2
let (_ : unit event) = connect x3 send_y2

but as I understand it it is forbidden to call send in the E.map function. Is there another way of getting this kind of behaviour?

debugging feature: output dependency graph

it would help debugging the logic of a react program to be able to see the full dependency graph in some way. especially if signals/events are created dynamically. for instance one could think of creating a DOT file by walking the dependencies, where the nodes would be labeled by variable name and signal/event type. this could then be visualized nicely by available tools.

i'm not sure this is easy to do since at runtime not all of the required information may be available? just a suggestion - feel free to close.

Subtyping of reactive values

I've been in a situation in my codebase where I wanted subtyping of reactive values. I solved it by making a new event of the supertype, but this is not as elegant.

My guess is that it is not possible to solve this with the current implementation (using references) + OCamls typesystem, as it makes the reactive type invariant.

I had an idea where I wondered wether OCaml's typesystem could be made to track the usage of the references internel to a module with an abstract type definition, and then not restrict the abstract type to be invariant.
This would let someone making a library able to use whatever implementation deemed neccesary, and not restrict the types exposed.

I also wonder whether the tracking of locality of references in function bodies, in the OCaml effects branch, would be able to solve this...

The semantics I would want of reactive values (the type 'a t is the analogue to e.g. 'a E.t):

type ab = [ `A | `B ]
type b = [ `B ]
type 'a t = T of 'a | TEmpty

let f : 'a t list -> 'a t = function
  | hd :: _ -> hd
  | [] -> TEmpty

let this_works =
  let a : ab t = T `A in
  let b :  b t = T `B in
  f [ a; (b: b t :> ab t) ]

What doesn't work:

module type S = sig
  type +'a t
  val v : 'a -> 'a t
  val fl : 'a t list -> 'a t
end
  
module T : S = struct
  type +'a t = T of ('a ref) | TEmpty
  let v x = T (ref x)
  let fl = function
    | hd :: _ -> hd
    | [] -> TEmpty
end

(* Error: In this definition, expected parameter variances are not satisfied.
 *        The 1st type parameter was expected to be covariant,
 *        but it is injective invariant. *)

Higher-order signals and equality

One of the annoyance of working with higher-order signals (i.e. signals carrying signals or events) is that the eq parameter has to be specified explicitly in the combinators that carry these values with ( == ), otherwise update steps will fail now and then with a hard to track down:

 Exception: Invalid_argument "equal: functional value".

We could provide two module Se, Ss that specialize the combinators for signals and events using S.Make. But this is similar to have to write eq:( == ) you need to remember you need to use this alternate set of combinators for such signals and this puts an unwanted burden on the programmer.

I'm not sure something can be done (well implicits, I'm not sure I like the idea of implicits but in that case that would solve it). The problem is that interesting FRP programs are higher-order.

Better detection of forbidden recursive update steps

In general it is not allowed to update primitives from an update step. If this happens and two update steps get intermingled it seems we get assertions errors from react.ml which lead to believe there's a bug in react itself. Can we report better errors while keeping react free of any global data structure ?

Besides better guidelines should be provided on how to avoid that. I think the best way of handling this is through interaction with a monadic concurrency library. Instead of having side effecting events at the outputs of the reactive system we should convert occurences to futures and that future representing the occurence should always and unconditionally immediately defer/yield which will bring the update step to an end (rather than the update step potentially continuing to execute in the future value which may trigger primitive updates and blow the whole system).

S.Option.value and S.const

The documentation states that S.Option.value (Always x) s is x whenever s is None. As such, if s is always None, we would expect to get back x:

let x, set_x = S.create 0
let s = S.const None           (* strange *)
(* let s, _ = S.create None *) (* correct *)

let o = S.Option.value ~default:(`Always x) s

let t = S.l2 (fun x o -> Printf.printf "x = %i, o = %i\n%!" x o) x o

let () = List.iter set_x [1;2]

Outputs:

x = 0, o = 0
x = 1, o = 0
x = 2, o = 0

If we switch S.const for S.create, we get the expected output:

x = 0, o = 0
x = 1, o = 1
x = 2, o = 2

This behaviour appears to be tested.

react fails to build with 4.14.0+trunk

I get the following error log when I try to build react with 4.14.0+trunk and dune.2.9.0

#=== ERROR while compiling react.1.2.1 ========================================#
# context     2.0.8 | linux/x86_64 | ocaml-variants.4.14.0+trunk | https://opam.ocaml.org#1fc79772
# path        ~/.opam/4.14.0+trunk/.opam-switch/build/react.1.2.1
# command     ~/.opam/opam-init/hooks/sandbox.sh build ocaml pkg/pkg.ml build --dev-pkg false
# exit-code   1
# env-file    ~/.opam/log/react-2325867-d2c37b.env
# output-file ~/.opam/log/react-2325867-d2c37b.out
### output ###
# [...]
# findlib: [WARNING] Interface topdirs.cmi occurs in several directories: /home/sk/.opam/4.14.0+trunk/lib/ocaml, /home/sk/.opam/4.14.0+trunk/lib/ocaml/compiler-libs
# File "src/react_top.ml", line 7, characters 17-33:
# 7 | let () = ignore (Toploop.use_file Format.err_formatter "react_top_init.ml")
#                      ^^^^^^^^^^^^^^^^
# Error: Unbound value Toploop.use_file
# Command exited with code 2.
# pkg.ml: [ERROR] cmd ['ocamlbuild' '-use-ocamlfind' '-classic-display' '-j' '4' '-tag' 'debug'
#      '-build-dir' '_build' 'opam' 'pkg/META' 'CHANGES.md' 'LICENSE.md'
#      'README.md' 'src/react.a' 'src/react.cmxs' 'src/react.cmxa'
#      'src/react.cma' 'src/react.cmx' 'src/react.cmi' 'src/react.mli'
#      'src/react_top.a' 'src/react_top.cmxs' 'src/react_top.cmxa'
#      'src/react_top.cma' 'src/react_top.cmx' 'src/react_top_init.ml']: exited with 10

S.sample seems to re-trigger its event when signal's value changes

Consider the following example:

let (event, trigger) = React.E.create ()
let (activated, activate) = React.E.create ()

let s = React.S.fold (+) 0 event

let handle i =
    trigger 1;
    print_int i

let trace = React.E.trace handle (React.S.sample (fun () e -> e) activated s)

After having loaded the above to utop and calling activate (), the program seems to fall into an infinite loop. The only explanation I can come to is that S.sample considers the event to "last" until handle finishes and since the latter updates s, S.sample in turn triggers E.trace with new value.

Is this behavior intentional? From my point of view it looks more like a bug.

Bug in the dynamic creation of S.{diff,changes} and S.Bool.{edge_detect}

Who wrote that shit ?

Since in these combinators, if we are in an update step, the dependency is only added at the end of the step when everything already updated, the rank used to create the mutable may no longer be accurate if m updates its rank in the step (since m' is not a dep yet, its rank will not have been affected by the rank update of m). So whenever we finally add the dependency, we need to update the rank of m' with the successor of m at that point again.

S.Bool.flip observes an invisible change

The documentation states that S.Bool.flip true cx can be false initially if cx triggers at time 0 (this is perfectly reasonnable.) In the following, the fact that cx triggers at time 0 seems to only be observable by S.Bool.flip (this is less reasonnable.)

let x, set_x = S.create 0

let counter = ref 0

let err = S.bind x (fun v ->

    incr counter ;
    let id = !counter in
    Printf.printf "Update step %i\n%!" id ;

    let cx = S.changes x in (* new dynamic signal, so x hasn't change yet *)
    let test_flip = S.Bool.flip true cx in (* should be true initially *)

    let s =
      S.l2 (fun b w ->
             Printf.printf "[%i] b = %s, w = %i\n%!" (* b = false, w = 42 *)
                id (if b then "true" else "false") w)
        test_flip
        (S.hold 42 cx) in

    let e = (* never prints *)
      E.l1 (fun w -> Printf.printf "[%i] event w = %i\n%!" id w) cx in

    ignore (S.retain s (fun _ -> ignore e)) ;

    s)

let () =
  List.iter
    (fun v -> Gc.full_major () ; Printf.printf "---\n%!" ; set_x v)
    [1;2]

Outputs (*) :

Update step 1
[1] b = true, w = 42

---
Update step 2
[2] b = false, w = 42

---
Update step 3
[3] b = false, w = 42

So neither S.hold nor E.map sees the event at time 0. The exceptional behaviour of flip is tested, so maybe I'm missing something?


(*) There is a small leak of previous signals/events, so the real output is:

Update step 1
[1] b = true, w = 42

---
Update step 2
[1] event w = 1
[1] b = false, w = 1
[2] b = false, w = 42

---
Update step 3
[2] event w = 2
[2] b = true, w = 2
[3] b = false, w = 42

Make update steps more robust to user exceptions

Real world long running programs may not want to fail on unexpected exceptions. As it stands if an exception is raised by a user defined signal or event it usually renders the whole reactive system inoperant (e.g. mutables will still be associated to a step that failed hence preventing further new schedules). Without looking it into deeply I see two approaches:

  1. Catch exceptions at the level of the update function call and trap them to a user defined
    trap. E.g. like fut does [1][2].
  2. Let it flow until execute and cleanup the step (go through the step's priority queue and set the update step stamp of the nodes to Step.nil.).

In general 2. may break much more invariants when an exception occurs than 1. For example it may break the semantics of nodes unrelated to the one that raised (but just happen to be a dependent of the same node). With 1. only the nodes that raises and its dependents break, so I think I'd prefer to go along the lines of 1.

As a side effect we should also try to see if there's something we can do to report better information about the source of the raise, currently it always report the start in react.ml which is not that helpful (note, this doesn't seem to be the case in fut, why?)

Dynamic E.merge

It's currently expensive to merge a dynamic collection of events: The E.merge function only works on a fixed list, so on each change to the list, we need to re-merge it from scratch (and the cost is proportionate to the number of elements in the list, rather than the number of added events.) Alternatively, we can merge the previously merged event with the new ones: This time, we pay a slow down proportionate to the number of updates to the list.

Would it be possible to have an analogue to E.switch, that would compute the "merge" of the inner events efficiently? (rather than their sequencing)

val union : ('a -> 'a -> 'a) -> 'a event -> 'a event event -> 'a event

(* or fold-like, but it looks confusing *)
val union : ('a -> 'b -> 'a) -> 'a -> 'b event -> 'b event event -> 'a event

I personally only care about union f when f is commutative, and I don't believe there's a serious use case for an "ordered dynamic merge", but that's discussable (in practice, the commutative version can be faster -- and the ordered one is recoverable from it.)

"Update step" two-stepped update

The update steps provide a nice mechanism for synchronizing primitive updates. However, it has an odd semantics since the actual effects are splitted between updating the events/signals and propagating the change:

let x, set_x = S.create "old" in
let s1 = Step.create () in
Printf.printf "x = %s\n%!" (S.value x) ; (* x = old *)
set_x ~step:s1 "new" ; (* do not propagate, but do update x *)
Printf.printf "x = %s\n%!" (S.value x) ; (* x = new *)
Step.execute s1; (* propagate *)

While this is perfectly documented, it seems counter-intuitive: The update steps interface gives the illusion that the updates are synchrone, but that's not really the case. If the update were delayed until the actual execution of the step, some edge cases would disappear:

  • Currently, we can't prepare two update steps at the same time, since their update is effectful:
let s1 = Step.create () in
let s2 = Step.create () in
set_x ~step:s1 "v1" ;
set_x ~step:s2 "v2" ; (* raises Invalid_argument "already scheduled" *)
  • Calling set_x ~step inside another update step is totally unsafe: it lets us rewrite the current value (rather than raising an exception) which breaks the semantics and (may) trigger an error much later. If the affectation was delayed, the documentation would also be simpler since only Step.execute would really be unsafe and you wouldn't need to clarify the two-step affect/propagate process. (Well, the old api that doesn't require an explicit step would still be unsafe, since it calls execute under the hood.)

In general, React has quite a few edge cases that one needs to recall, and their description pollutes the documentation and adds to the cognitive burden. I'm not convinced that this one is necessary, it looks like an implementation detail rather than the expected semantic... Is there a use case where the current behaviour is useful?

(This is a minor issue, since React doesn't push you towards juggling with update steps, which explains why the step's affectations aren't noticeable at first sight. It has more to do with clean api design and simplifying the documentation.)

Research S.value alternatives.

While using S.value isn't "good style" from a pure FRP perspective, some advanced tasks seems to benefit from it. I'm interested in use-cases like the ReactiveData library (which uses it here for example.)

In practice, S.value raises in a rather random way, which makes it hard to structure the code so that it just works. Without a consistent S.value, we can't implement a simple "derivate" algorithm, since the initial value is unobservable.

val derive : t signal -> t * patch event
                         ^
                         requires S.value to work

The not-builtin snapshot/freeze function for signals allow us to write the less obvious:

t signal -> t signal * patch event
            ^^^^^^^^
            a constant signal which stores the value at time 0

which is a lot less convenient for the rest of the code base. For example, "integrating" the event patchs back into a signal of values ends up like:

val integrate : t signal * patch event -> t signal

let integrate (initial, patches) =
  S.switch
    (S.fold
       (fun ts p -> S.map (apply_patch p) ts)
       initial
       patches)

while this would be trivial with a real value:

let integrate initial patches =
  S.fold apply_patch initial patches

This pain is propagated to each function that works on the patched representation and it generates some confusion (we have to remember that the signal must be constant to understand what's going on.) There are other examples where S.value is tempting (it's part of the library after all), and while it's always possible to find an alternative, it's not really worth the brain melting.

From a semantic perspective, it also means that signals (can) have an initial Unitialized value, followed by their first real value, and then potentially followed by their other values. The S.changes function doesn't let us observe the Uninitialized -> Initialized transition in the signal, since it happens during an update step (it still is a change of value, just not one that's observable.) Ideally, the unset state shouldn't even exist, since it doesn't make any sense with the rest of the library!

Now, regarding the implementation, it sounds like S.value could "pull" the initial value when confronted with an uninitialized signal, rather than failing because the corresponding "push" hasn't been executed yet. Is there a situation where this would fail?

(To clarify, I have node.update (Step.find_unfinished node) in mind, but I don't fully understand the React codebase, so it might not be enough (?))


edit: I somehow missed the simpler:

S.bind initial (fun t -> S.fold apply_patch t patches)

So the collections' example might be workable without S.value after all. Still, it would be nicer if it worked properly :) .

event stream hangs for some reason

I'm trying to use event streams to map a stream of messages appearing in socket to the streams of db writings, notifications through websockets and so on. Everything was fine till I've added websocket support. After that the whole event stream started to hang on frequently appearing big messages. The problem appears only then I use websockets, though I suspect that it might be caused by some react's invariant being broken due to heavy computations or something else.

I've described the issue more verbosely here [1] and I hope you would introduce some clarity concern what may cause such issues. Thank you.

[1] vbmithr/ocaml-websocket#95

E.merge is slow

I'm trying to merge a large number of events, which are mostly passive (only one or two triggers on each step.) The complexity of E.merge makes this prohibitive, since it iterates on the full list to discover the events that triggered: it's O(len all) rather than O(len triggered). [and it doesn't remove the stopped signals.]

As a user, we can manually call E.merge in a binary fashion, to recover a O(log (len all) + len triggered). It's better, but it's not great and it adds a lot of overhead -- while not performing great if a large number of events do trigger at the same time. An alternative would be to provide an unordered merge in React: the result would be accumulated in a reference, that's updated by each triggered event. Finally, the "merged" event would trigger and reset the reference for the next update step. In pseudocode:

let observe xs = E.merge (fun _ _ -> ()) () xs

let unordered_merge f z xs =
  let ok = ref false in
  let acc = ref z in
  E.fmap
    (fun () ->
       if !ok
       then let v = !acc in (ok := false ; acc := z ; Some v)
       else None)
    (observe
      (List.map (E.l1 (fun x -> ok := true ; acc := f !acc x)) xs))

The ordered merge could be recovered by accumulating the (index, value), sorting them and then reducing:

let merge f z xs =
  E.l1
    (fun xs ->
       let xs = List.sort (fun (i, _) (j, _) -> compare i j) xs in
       List.fold_left (fun z (_, x) -> f z x) z xs)
    (unordered_merge (fun es e -> e::es) []
       (List.mapi (fun i -> E.l1 (fun x -> (i, x))) xs))

Now, if all the events generally triggers, then the current implementation is of course better since it doesn't have to sort. In my experience, this is a rare use-case: in fact, I only care about the unordered merge, since the reducing function is generally agnostic of the list order.

If the manual binary merge version is the prefered solution to this problem, then it would be nice to warn about the complexity of E.merge in the doc!

Assert failure after S.stop

The following code exhibits a small effect leak:

let x, set_x = S.create 0

let y =
  S.bind x (fun v ->
    let id = Random.int 10000 in
    let s = S.map (fun _ -> Printf.printf "hi %i\n%!" id) x in
    (* S.stop s ; *)
    s)

let () =
  List.iter
    (fun v -> Printf.printf "---\n%!" ; Gc.full_major () ; set_x v)
    [1;2]

Outputs:

hi 9344

---
hi 9344   <- leak from previous step
hi 6685

---
hi 6685   <- here too
hi 182

If we try to naively stop the inner signal early, we get:

hi 9344

---
Fatal error: exception Assert_failure("src/react.ml", 422, 51)

The documentation states that S.stop may not take effect immediately if executed during an update step: But here, S.stop does kill the signal early, which puts S.bind in trouble. I would expect the following output:

hi 9344

---
hi 6685

---
hi 182

If that's not possible, it would be nice to raise a proper exception and document when it is safe to call S.stop.

React doesn't build on 4.02.0dev+trunk

Error log:

===== ERROR while installing react.1.0.1 =====
# opam-version 1.1.1
# os           linux
# command      ocaml pkg/git.ml
# path         /home/whitequark/.opam/4.02.0dev+trunk/build/react.1.0.1
# compiler     4.02.0dev+trunk
# exit-code    2
# env-file     /home/whitequark/.opam/4.02.0dev+trunk/build/react.1.0.1/react-23422-58c514.env
# stdout-file  /home/whitequark/.opam/4.02.0dev+trunk/build/react.1.0.1/react-23422-58c514.out
# stderr-file  /home/whitequark/.opam/4.02.0dev+trunk/build/react.1.0.1/react-23422-58c514.err
### stderr ###
# File "pkg/git.ml", line 3, characters 0-1:
# Error: Syntax error

React is a dependency for utop, so currently utop can't be used on trunk.

Variation of switch for getting an event inside a signal?

I was looking for something like S.switch or E.switch but for an 'a event signal. So basically fun sig_of_events -> sig_of_events |> S.changes |> E.switch E.never. Is this available in some form directly? Is it a bad idea for some reason?

Don't leak when weak arrays are not weak (i.e. in javascript)

We can also perform the manual memory management at the leaves of dependency graph as froc or perc do.

If we know a signal is no longer to be used we can use the existing {E,S}.stop functions. For now the implementation just prevent further updates to the node and clears the dependents weak array.

But we can walk back to the producers of the node, remove ourselves from the dependency array of the producer, if the dependency array is empty, we can also stop this node (and recursively).

For that we add a new optional parameter strong to stop functions:

E.stop : ?strong:bool -> 'a event -> unit
S.stop : ?strong:bool -> 'a signal -> unit

If strong is false (default), the old semantics is preserved, if true the above procedure is applied.

Installation without opam

I want to install react on Windows, where opam is not available. Please give instructions on how it can be done easily. If possible, include a Makefile for that purpose in the distribution.

Allow simultaneous primitive occurences

Expose update steps:

module Step : sig
  type t
  create : unit -> step
  execute : step -> unit
end

Signal setter and event sending functions will now accept an optional step parameter:

val S.create : unit -> 'a signal * (?step:Step.t -> 'a -> unit)
val E.create : unit -> 'a event * (?step:Step.t -> 'a -> unit)

If the step parameter is not provided, one is automatically created and performed and the old semantics is preserved. If provided the update is scheduled on the step and the update is only performed when Step.execute is invoked on the step.

a signal that monitors a ref?

i just came across a case where i wanted to have a signal so that S.value s gives !r where r is some pre-existing ref value. conceptually signals and refs are quite similar, so it seems this could be a handy bridge to the non-reactive world? or is this unsound since the signal may change unnoticed? i could make a const signal but then i would need to use !(S.value s); is this all that makes sense?

react.1.2.1 build for OCaml 5.00.0+trunk

The Pervasives module has been deprecated in OCaml 5.00.0+trunk, and the recommendation is to use Stdlib instead. react.1.2.1 builds fine for OCaml 5.00.0+trunk with this change.

Would it be possible for you to review the patch, or suggest any other alternative?

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.