Comments (41)
I propose as follows:
- The syntax of a
TypedFunctionTest
is extended to allow, for example,function(item(), %optional xs:integer) as xs:boolean
. This is essentially the union offunction(item())
andfunction(item(), xs:integer)
: it matches both arity-1 functions with signaturefunction(item()) as xs:boolean
and arity-2 functions with signaturefunction(item(), xs:integer) as xs:boolean
. If a parameter is designated%optional
then all subsequent parameters must also be designated%optional
. - A specific
FunctionItem
still has a fixed arity. A type may allow functions with different arities, but each instance has a fixed arity, and a dynamic function call must supply the correct number of arguments. - In the function coercion rules, we drop the arity-bending coercion. If the signature of a higher-order function declares a callback parameter of type
function(item(), %optional xs:integer) as xs:boolean
then (subject to the old function coercion rules) the supplied argument must match this type, which means it must match one of the two allowed possibilities. - To make it easier for user-defined functions to invoke a callback that allows functions of different arities, we extend
fn:apply()
so that excess arguments in the argument array are dropped. So iffn:apply($f, [$arg1, $arg2])
supplies an array of two arguments to a function item that only expects one, the second argument is silently ignored. - In those F&O functions where we have added an optional
$position
argument, this will be declared as%optional
in the type signature. In typical cases such asfn:filter
the first argument will revert to being mandatory, which means fn:filter will no longer accepttrue#0
andfalse#0
as arguments. (Discuss.)
from qtspecs.
What is wrong with having two overloads?
We would need a completely different type system. We don't have that kind of polymorphism in the language and it would be an immense effort to introduce it, especially if we were to retain any kind of backwards compatibility.
What we could potentially do would be to introduce a general union type so the signature could be
filter($input as item()*, $predicate as any-of(function(item()) as boolean, function(item(), integer) as boolean)
That's essentially equivalent to what I'm proposing.
There's a world of difference between having one function that accepts different kinds of arguments, and having two functions where the function despatch algorithm depends on the type of the supplied arguments.
You're constantly comparing with C#, which is a strongly typed language. For better or worse, that's not where we are coming from. We are much closer to weakly typed languages like Javascript.
from qtspecs.
Except that this still requires the call to be:
funName(3, ())
The signature of fn:sum
is:
fn:sum(
$values as xs:anyAtomicType*,
$zero as xs:anyAtomicType? := 0
) as xs:anyAtomicType?
…and you can omit the second argument and write fn:sum(1)
. We could apply a similar principle to function items.
from qtspecs.
I've wondered why function types don't allow named parameters. That's a separate issue, but allowing (optional) named parameters on type signatures would help with documenting the behaviour of the function.
That’s true.
Your issue on supporting optional parameters on dynamic functions is still open as well (#158). I believe to remember that we observed it’s more challenging to realize/formalize this for dynamic function items.
from qtspecs.
I strongly support the idea that we need to make the language type-safe where this is possible.
I agree in principle (although I encountered use cases in practice where a 0-arity function like true#0
can be useful argument; see #981 (comment)).
A pragmatic solution would be to simplify the signatures to…
fn:xyz(
$input as item()*,
$action as function(*)
)
…and define the supported types and supported arities for $action
in the detailed rules of the corresponding function. A type error could be raised if the argument doesn’t match these rules (but it will be difficult for IDEs to raise such errors statically).
If we generalized local union types, the following syntax would become legal…
fn:xyz(
$input as item()*,
$action as union(
function(item()) as xs:boolean,
function(item(), xs:integer) as xs:boolean
)
)
…but pretty verbose.
from qtspecs.
Unfortunately the syntax %optional
does have complications because a SequenceType
can already start with the annotation %optional
. We could get around that if we want to by reserving the annotation name. However, I chose the syntax with the thought that we might eventially allow general annotations on function parameters, and that appears to be tricky. Perhaps we should use %%optional
.
from qtspecs.
Point 4, silently dropping arguments in fn:apply()
, makes me a little nervous if this applies in the general case. If I think I'm applying a function with three arguments, but I've identified a function that only takes two, that's probably an error on my part.
Did you intend, or could it be written, to apply only in cases where the extra arguments are %optional
(or, ugh %%optional
)?
from qtspecs.
Well we could generalise fn:apply()
so that instead of taking an array as the second argument, it takes a function which, given an integer N, returns the value to be used for the Nth argument - explicit lazy evaluation. Would that make you more comfortable? (That's backwards compatible because we had the foresight to ensure that an array can be used where a function-taking-an-integer-argument is expected).
from qtspecs.
I’m not keen on adding even more syntax for this. It would need to be correctly understood again, and it would be yet another approach to tackle optional arguments. In static functions, arguments are optional if default arguments exist…
declare function predicate(
$item as item()*,
$position as xs:integer := ()
) as xs:boolean
…so (if we think we need) we should rather choose a similar solution for function items.
Next, if we make the first argument(s) mandatory again, we would need to revive some discussions that have been resolved in the past, e.g. on fn:identity
(#858).
But I definitely agree that the documentation of functions with higher-order function arguments gets more and more arcane, no matter if arguments may be more or less optional. I think we can win a lot by improving the presentation of the arguments of higher-order functions in general.
We could start by adding comments for function item arguments.
fn:filter(
$input as item()*,
$predicate as function(
item(), (: current item :)
xs:integer (: current position :)
) as xs:boolean
) as item()*
This is how the filter
function is presented in JavaScript; maybe we can learn from them?
from qtspecs.
I propose as follows:
- The syntax of a
TypedFunctionTest
is extended to allow, for example,function(item(), %optional xs:integer) as xs:boolean
. This is essentially the union offunction(item())
andfunction(item(), xs:integer)
: it matches both arity-1 functions with signaturefunction(item()) as xs:boolean
and arity-2 functions with signaturefunction(item(), xs:integer) as xs:boolean
. If a parameter is designated%optional
then all subsequent parameters must also be designated%optional
.- A specific
FunctionItem
still has a fixed arity. A type may allow functions with different arities, but each instance has a fixed arity, and a dynamic function call must supply the correct number of arguments.- In the function coercion rules, we drop the arity-bending coercion. If the signature of a higher-order function declares a callback parameter of type
function(item(), %optional xs:integer) as xs:boolean
then (subject to the old function coercion rules) the supplied argument must match this type, which means it must match one of the two allowed possibilities.- To make it easier for user-defined functions to invoke a callback that allows functions of different arities, we extend
fn:apply()
so that excess arguments in the argument array are dropped. So iffn:apply($f, [$arg1, $arg2])
supplies an array of two arguments to a function item that only expects one, the second argument is silently ignored.- In those F&O functions where we have added an optional
$position
argument, this will be declared as%optional
in the type signature. In typical cases such asfn:filter
the first argument will revert to being mandatory, which means fn:filter will no longer accepttrue#0
andfalse#0
as arguments. (Discuss.)
It seems a lot more simple to just split this into two separate overloads - as this is done in C# and probably in other languages.
Thus we will avoid having to create a complicated machinery and we will be able to keep the signatures for each of the overloads more readable and easily understandable.
What is wrong with having two overloads?
From Microsoft's documentation:
Overloads
Where(IEnumerable, Func<TSource,Boolean>)
Filters a sequence of values based on a predicate.
Where(IEnumerable, Func<TSource,Int32,Boolean>)
Filters a sequence of values based on a predicate. Each element's index is used in the logic of the predicate function.
from qtspecs.
We would need a completely different type system. We don't have that kind of polymorphism in the language and it would be an immense effort to introduce it, especially if we were to retain any kind of backwards compatibility.
If we are facing this kind of difficulties, then I'd rather prefer the $action
function type to be specified as function(*)
, and still say in the Spec that this is equivalent to having two separate overloads and list each of these overloads in full.
Thus readability will not suffer, we will continue to be "dynamic" - seems like a win-win situation, doesn't it?
from qtspecs.
I think this thread was catalyzed by discussion on fn:fold/scan-left/right
. Can we get a list of all XPath functions that might be relevant/affected?
from qtspecs.
Can we get a list of all XPath functions that might be relevant/affected?
A quick (and possibly incomplete) search of the function catalog gives
for-each filter fold-left fold-right while-do do-until for-each-pair index-where for-each filter fold-left fold-right for-each-pair every index-where some partition
from qtspecs.
A quick (and possibly incomplete) search of the function catalog gives […]
…and basically every other function that has function arguments. For example, the action of fn:replace
has two parameters, both of which could be regarded as optional.
I'm interested what some of you think about my suggestion to focus on our documentation? JavaScript users (and there are lots of them) seem to have no problem with the solution that we're about to take.
from qtspecs.
Instead of polluting the definition of well-known and understood functions by adding a new argument that nobody seems to use (I tried hard to find examples of using the index argument!) let us create a separate function that has this extended capabilities - for example we could use a name like: fold-left-extended
or fold-left-position-aware
.
The mere fact that it is so difficult finding examples of actually using this index-argument clearly tells us that this is a separate, much limited functionality.
Let us not pollute some of the most-popular and well-known functions in functional programming, making them unrecognizable and difficult to understand.
from qtspecs.
As I said at the start of the thread, the main issue is making it clear to the reader what the options are, and we don't necessarily need to change the language to do that: we could go with a documentation solution. Building on Christian's suggestion, something like:
fn:filter(
$input as item()*,
$predicate as function(
item(), (: current item :)
xs:integer (: current position - optional :)
) as xs:boolean
) as item()*
from qtspecs.
Should line 5 be...
xs:integer? := () (: current position - optional :)
?
from qtspecs.
As I said at the start of the thread, the main issue is making it clear to the reader what the options are, and we don't necessarily need to change the language to do that: we could go with a documentation solution. Building on Christian's suggestion, something like:
fn:filter( $input as item()*, $predicate as function( item(), (: current item :) xs:integer (: current position - optional :) ) as xs:boolean ) as item()*
This is still quite unreadable - but we were talking more specifically about the need for having such additional/optional argument for the fold - functions. And there there is absolutely insignificant amount of evidence (concrete code examples of usage) that adding such additional, new argument is really needed in the case of the fold functions.
If the new argument would be specified in less that 0.1% of all cases, this clearly means that if (for whatever reason) somebody (whoever they may be) desperately needs it, then they can have a separate new function, say fold-left-extended
If 80% of the world already knows well the fold functions from languages such as Haskell and C#, why would we want to disguise our fold functions and make them completely unrecognizable to this majority of people?
Maybe we intentionally want to repel them from using our languages?
from qtspecs.
Should line 5 be...
xs:integer? := () (: current position - optional :)
?
It could look like that if we decide to add default arguments to dynamically called function items in the future (but there are various implications that would need to be solved).
With the current solution in the spec, both arguments are optional, i.e. you can also supply a function like fn() { true() }
as predicate (see function coercion for details).
from qtspecs.
For those who want to learn about use cases for folds with index access, two examples in JavaScript:
https://stackoverflow.com/questions/35034006/javascript-array-reduce-start-from-index
https://blog.alexdevero.com/javascript-reduce-method/
from qtspecs.
I would be happy to go with something that's a bit more type-safe than Javascript, i.e. not allowing arguments to be omitted unless there's something explicit to say they're optional. But I certainly don't accept the argument that something we've adopted from Javascript is going to be unacceptable to our user community.
from qtspecs.
but we were talking more specifically about the need for having such additional/optional argument for the fold - functions
Not on this thread we weren't. This thread is about how to indicate in function signatures that some arguments of callback functions are optional. The question of whether to have such optional arguments on the fold
and scan
functions is another issue entirely.
from qtspecs.
I would be happy to go with something that's a bit more type-safe than Javascript, […].
True. At the moment, it’s convenient to pass something as simple as true#0
as a dynamic argument to a function…
let $select := function($test) {
fn:filter($some-data, $test)
}
return $select(true#0)
…but if the first parameter of the predicate function of fn: filter
was mandatory, passing fn { true() }
or fn($i) { true() }
would certainly be ok, and possibly clearer.
Regarding the syntax, @Arithmeticus’ observation sticks in my mind: Wouldn't it be best and consistent if we introduced default values for function items (as @rhdunn had suggested a long time ago)? Which would certainly be one more hard nut to crack.
from qtspecs.
but we were talking more specifically about the need for having such additional/optional argument for the fold - functions
Not on this thread we weren't. This thread is about how to indicate in function signatures that some arguments of callback functions are optional. The question of whether to have such optional arguments on the
fold
andscan
functions is another issue entirely.
OK, I will be happy just not to add this new argument to the $action
function of the folds/scans.
For all other functions, still we need to design their signatures having in mind the importance of readability and understandability.
from qtspecs.
For those who want to learn about use cases for folds with index access, two examples in JavaScript:
https://stackoverflow.com/questions/35034006/javascript-array-reduce-start-from-index https://blog.alexdevero.com/javascript-reduce-method/
These only prove the point that the use of the artificially added new index argument is extremely rare . The blog article proposes a solution for computing the average that uses completely unnecessarily the index argument. This, of course is not needed to find the average. And there is no other, example in all the provided 5 examples that uses the index-argument.
As for the StackOverflow question - it was asked 8 years ago, and of the 3 solutions that use the reduce
function , only one uses the index argument. Again, this shows that the index argument is not necessary in this case.
Also, in the last 8 years approximately 2136 Javascript questions were asked on StackOverflow. Only one solution to only one of these tried to use (unnecessarily) the index argument This is 0.05% of all questions, and 0.01% of all solutions. These numbers speak for themselves.
To summarize: The provided "evidence" actually tells us that calling the $action
function with the index argument is extremely rare, and even when this has been done, it was completely unnecessary.
from qtspecs.
The provided "evidence" […] it was completely unnecessary.
Examples and evidence are different things.
Conversations are pointless if the mind is fixed. Let’s focus on the actual topic of this issue.
from qtspecs.
The provided "evidence" […] it was completely unnecessary.
Examples and evidence are different things.
Conversations are pointless if the mind is fixed. Let’s focus on the actual topic of this issue.
Still the fact remains that it is difficult to find usage of this parameter and even when found this usage is artificial and unnecessary.
As @michaelhkay pointed out, this belongs to the other issue - hope we can keep the folds clean.
from qtspecs.
I propose as follows:
- The syntax of a
TypedFunctionTest
is extended to allow, for example,function(item(), %optional xs:integer) as xs:boolean
. This is essentially the union offunction(item())
andfunction(item(), xs:integer)
: it matches both arity-1 functions with signaturefunction(item()) as xs:boolean
and arity-2 functions with signaturefunction(item(), xs:integer) as xs:boolean
. If a parameter is designated%optional
then all subsequent parameters must also be designated%optional
.- A specific
FunctionItem
still has a fixed arity. A type may allow functions with different arities, but each instance has a fixed arity, and a dynamic function call must supply the correct number of arguments.- In the function coercion rules, we drop the arity-bending coercion. If the signature of a higher-order function declares a callback parameter of type
function(item(), %optional xs:integer) as xs:boolean
then (subject to the old function coercion rules) the supplied argument must match this type, which means it must match one of the two allowed possibilities.- To make it easier for user-defined functions to invoke a callback that allows functions of different arities, we extend
fn:apply()
so that excess arguments in the argument array are dropped. So iffn:apply($f, [$arg1, $arg2])
supplies an array of two arguments to a function item that only expects one, the second argument is silently ignored.- In those F&O functions where we have added an optional
$position
argument, this will be declared as%optional
in the type signature. In typical cases such asfn:filter
the first argument will revert to being mandatory, which means fn:filter will no longer accepttrue#0
andfalse#0
as arguments. (Discuss.)
I think this is a good and clear proposal and it is a positive step in solving the issue.
I would prefer to have not:
function(item(), %optional xs:integer) as xs:boolean
but the easier to read (no '%' character):
function(item(), optional xs:integer) as xs:boolean
Or even:
function(item() [, xs:integer]) as xs:boolean
from qtspecs.
I like the idea of using the :=
syntax for this, instead of providing yet another syntax:
function(item(), xs:integer := 0) as xs:boolean
That would resolve the issue of what value should be supplied if the function is called with the lower arity.
From an API perspective, should we also make xs:integer
an optional sequence? I.e.:
function(item(), xs:integer? := ()) as xs:boolean
That's clearer to the user that the second parameter is optional, even if it will never be called with an empty value.
from qtspecs.
I like the idea of using the
:=
syntax for this, instead of providing yet another syntax:function(item(), xs:integer := 0) as xs:boolean
That would resolve the issue of what value should be supplied if the function is called with the lower arity.
This doesn't resolve the issue of providing a function with arity 2, when a function with arity 3 is expected. "Forgiving" this results in a flood of unintentional user errors going unnoticed and resulting in difficult to debug runtime errors.
From an API perspective, should we also make
xs:integer
an optional sequence? I.e.:function(item(), xs:integer? := ()) as xs:boolean
That's clearer to the user that the second parameter is optional, even if it will never be called with an empty value.
Except that this still requires the call to be:
funName(3, ())
and not
funName(3)
from qtspecs.
Except that this still requires the call to be:
funName(3, ())
The signature of
fn:sum
is:fn:sum( $values as xs:anyAtomicType*, $zero as xs:anyAtomicType? := 0 ) as xs:anyAtomicType?…and you can omit the second argument and write
fn:sum(1)
. We could apply the same principle to function items.
And what sensible default would there be for the $predicate
argument in the case of:
array:filter(
$predicate as function(item()*, xs:integer) as xs:boolean
) as array(*)
Wouldn't it be nice if we could provide as the default a 1-argument function? No, because there are so many such 1-argument predicates and each could be handy. Here the real question is not about the default value for a function, or for a function argument. What we really want somehow to do, is specify the default arity of a function, when this arity can vary from one call to another.
Even if we specify that one argument is optional, the function still must be of of arity 2, and be able to handle the 2nd argument's (default) value.
In many cases when a function of N - 1 arguments is passed N arguments this could be an user error. By masking this error we are doing a bad service to the user, as they will be allowed to execute and will observe unexpected, strange and difficult to explain runtime behavior of their code.
from qtspecs.
And what sensible default would there be for the
$predicate
argument in the case of:array:filter( $predicate as function(item()*, xs:integer) as xs:boolean ) as array(*)
I would propose xs:integer := ()
, as the value will not be used.
Even if we specify that one argument is optional, the function still must be of of arity 2, and be able to handle the 2nd argument's (default) value.
Currently, due/thanks to the function coercion rules, fn:filter($value, true#0)
is valid.
Personally, I believe our major challenge is the improvement of the existing documentation. Think e.g. of:
fn:replace(
$value as xs:string?,
$pattern as xs:string,
$replacement as xs:string? := (),
$flags as xs:string? := '',
$action as (function(xs:untypedAtomic, xs:untypedAtomic*) as item()?)? := ()
) as xs:string
By just reading the signature, it’s impossible to guess what the parameters of $action
are supposed to do, no matter what the default arity would be. And things won’t get easier when more high-order functions will be added in the future.
from qtspecs.
I've wondered why function types don't allow named parameters. That's a separate issue, but allowing (optional) named parameters on type signatures would help with documenting the behaviour of the function.
from qtspecs.
And what sensible default would there be for the
$predicate
argument in the case of:array:filter( $predicate as function(item()*, xs:integer) as xs:boolean ) as array(*)I would propose
xs:integer := ()
, as the value will not be used.
An integer value cannot be the empty sequence.
from qtspecs.
Even if we specify that one argument is optional, the function still must be of of arity 2, and be able to handle the 2nd argument's (default) value.
Currently, due/thanks to the function coercion rules,
fn:filter($value, true#0)
is valid.
This is even worse!
Passing a 0-argument function where a 1-argument function (which is the majority of use cases) or a 2-argument function is expected.
This is meaningless and misleading when the main goal of fn:filter
is considered.
from qtspecs.
This is even worse!
Passing a 0-argument function where a 1-argument function (which is the majority of use cases) or a 2-argument function is expected.
All this has already been discussed in the past.
from qtspecs.
You're constantly comparing with C#, which is a strongly typed language. For better or worse, that's not where we are coming from. We are much closer to weakly typed languages like Javascript.
Whether or not we should closely copy Javascript is a separate topic (which probably we need to discuss separately and reach some conclusion/agreement on), but even if this is so imperative to do, why not copy from the "bigger brother" Typescript, which allows function overloads?
Function Overloads in Typescript.
Why did they do this? Clearly not for the convenience of the implementors (who, in our case, (said in comments) don't care) but for the Typescript programmers, who need to both easily read the specification of someone else's function and also be able to specify their own-created functions in the most clear and understandable way to future users.
In Typescript it is easy to raise a compile-time error when a function is called with 2 arguments, but no overload expects 2 arguments, there are just 2 overloads, expecting either 1 or 3 arguments.
I deeply suspect that we are not able to have such level of validation/diagnostics at the moment.
from qtspecs.
@dnovatchev The point is that XPath, XQuery, and XSLT function overloading is only based on the arity of the function. Likewise, for types it coerces (matches) values not types.
A such, it is not possible to define two functions with the same arity but different parameter types. So they cannot support one taking a function(item()) as xs:boolean
and another taking a function(item(), xs:integer) as xs:boolean
.
Doing so now would break a lot of code and existing implementations, so is not technically feasible in XPath, XQuery, and XSLT.
Incidentally, that's the same roadblock that the creators of TypeScript had, hence them creating a separate language to JavaScript!
from qtspecs.
In Typescript it is easy to raise a compile-time error when a function is called with 2 arguments, but no overload expects 2 arguments, there are just 2 overloads, expecting either 1 or 3 arguments.
Note that in Typescript it’s even discouraged to make parameters of callback (i.e., higher-order) functions optional, as it’s completely valid as well to provide callbacks that accept fewer arguments:
from qtspecs.
Perhaps it would also be useful to go beyond documentation, and attach some syntax and semantics to it. Specifically, if the signature of the callback function indicates that the first N arguments are required, then supplying a function item of arity less than N will result in a type error.
Well. any function could potentially be used as a callback - then following the above would mean not to specify at all required and optional arguments - that is: all arguments become optional.
Down the rabbit hole ...
I think the opening comment by @michaelhkay is absolutely correct (bold hi-lighting is mine):
Perhaps it would also be useful to go beyond documentation, and attach some syntax and semantics to it. Specifically, if the signature of the callback function indicates that the first N arguments are required, then supplying a function item of arity less than N will result in a type error.
I strongly support the idea that we need to make the language type-safe where this is possible.
from qtspecs.
@dnovatchev The point is that XPath, XQuery, and XSLT function overloading is only based on the arity of the function. Likewise, for types it coerces (matches) values not types.
A such, it is not possible to define two functions with the same arity but different parameter types. So they cannot support one taking a
function(item()) as xs:boolean
and another taking afunction(item(), xs:integer) as xs:boolean
.Doing so now would break a lot of code and existing implementations, so is not technically feasible in XPath, XQuery, and XSLT.
Doing so for ver. 4.0 and later will not break any existing pre-ver. 4.0 code.
It is true that having a function reference such as myFun#3
cannot distinguish between two overloads that have the same arity of 3.
But we could augment the current syntax for a named function reference so that the exact overload becomes part of it, for example something like:
myFun#3^1
meaning: the 1st overload that has arity 3
This is the first that comes to mind, maybe there could be even better ways to distinguish between different same-arity overloads.
We simply do need to do our job on this, and I believe we can achieve a good and useful result.
from qtspecs.
Related Issues (20)
- XQFO Code in the Rules sections HOT 7
- fn:subsequence-where: equivalent `fn:slice` expression HOT 3
- Reinstate subsequence-before HOT 1
- Errors in forming function items (continued) HOT 4
- fn:has-attributes HOT 5
- Rewrite spec of deep lookup operator: edits
- XPath Appendix I: Comparisons
- XQFO: Context item → value
- fn:parse-json, fn:json-to-xml: `number-parser`, `fallback`
- Rules for context-dependent function references in XSLT (e.g. regex-group#1) HOT 1
- scan-left, scan-right: position argument, array functions HOT 4
- fn:reduce (or fn:fold without initial value) HOT 5
- Numeric Comparisons HOT 8
- character sequence constructor 'a' to 'z' HOT 22
- Transitive closure on non-nodes HOT 2
- Invisible-xml - missing details HOT 1
- Invoking maps & arrays: allow sequences? HOT 5
- regular expression addition - lookbehind assertions and lookahead assertions HOT 2
- regular expression addition - comments HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from qtspecs.