Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Flow type system? #52

Open
slowli opened this issue Jul 17, 2017 · 6 comments
Open

Use Flow type system? #52

slowli opened this issue Jul 17, 2017 · 6 comments

Comments

@slowli
Copy link
Contributor

slowli commented Jul 17, 2017

Since we (supposedly) build secure software, we need to worry about type safety everywhere (including the client), and allow to take care of type safety for developers using the client. Flow seems like a straightforward way to accomplish this; although there are some alternatives (e.g., TypeScript or Elm, or even compiling Rust code directly to wasm or asm.js).

@deniskolodin Do you think Elm would be an optimal way to pursue? I'm a bit worried that a "fully" functional Elm's approach can have certain performance implications. For example, the tutorial on lists does not seem to unroll recursion, not even in the tail-form (i.e.,

length : List a -> Int
length list = length2 list 0

length2 : List a -> Int -> Int
length2 list acc =
  case list of
    [] ->
        acc
    first :: rest ->
        length2 rest 1 + acc

so it doesn't work with lists of length more than several thousand. Am I doing something wrong, or is this just how Elm rolls (heh)? Also, in general it seems Flow is a bit more supported than Elm, although I may be biased.

@therustmonk
Copy link
Contributor

@slowli Elm is awesome for large web-applications, but it's unsuitable for CPU-bound tasks like cryptography algorithms, because Elm produces smart wrappers to maintain functional features. For example Elm generates the following code for Exonum.newMessage binding:

var _exonum$exonum_ico_client$Bind_Exonum$newMessage = function (declaration) {
	var struct = A2(
		_elm_lang$core$Json_Decode$decodeValue,
		_elm_lang$core$Json_Decode$value,
		_elm_lang$core$Json_Encode$object(
			{
				ctor: '::',
				_0: {
					ctor: '_Tuple2',
					_0: 'size',
					_1: _elm_lang$core$Json_Encode$int(declaration.size)
				},
				_1: {
					ctor: '::',
					_0: {
						ctor: '_Tuple2',
						_0: 'network_id',
						_1: _elm_lang$core$Json_Encode$int(declaration.networkId)
					},
					_1: {
						ctor: '::',
						_0: {
							ctor: '_Tuple2',
							_0: 'protocol_version',
							_1: _elm_lang$core$Json_Encode$int(declaration.protocolVersion)
						},
						_1: {
							ctor: '::',
							_0: {
								ctor: '_Tuple2',
								_0: 'service_id',
								_1: _elm_lang$core$Json_Encode$int(declaration.serviceId)
							},
							_1: {
								ctor: '::',
								_0: {
									ctor: '_Tuple2',
									_0: 'message_id',
									_1: _elm_lang$core$Json_Encode$int(declaration.messageId)
								},
								_1: {
									ctor: '::',
									_0: {
										ctor: '_Tuple2',
										_0: 'fields',
										_1: _exonum$exonum_ico_client$Bind_Exonum$serializeFields(declaration.fields)
									},
									_1: {ctor: '[]'}
								}
							}
						}
					}
				}
			}));
	var _p4 = struct;
	if (_p4.ctor === 'Ok') {
		return _exonum$exonum_ico_client$Native_Exonum.newMessage(_p4._0);
	} else {
		return _elm_lang$core$Native_Utils.crashCase(
			'Bind.Exonum',
			{
				start: {line: 116, column: 9},
				end: {line: 118, column: 45}
			},
			_p4)(_p4._0);
	}
};

//// BASIC STUFF ////

function F2(fun)
{
  function wrapper(a) { return function(b) { return fun(a,b); }; }
  wrapper.arity = 2;
  wrapper.func = fun;
  return wrapper;
}

function A2(fun, a, b)
{
  return fun.arity === 2
    ? fun.func(a, b)
    : fun(a)(b);
}
// ...
function A9(fun, a, b, c, d, e, f, g, h, i)
{
  return fun.arity === 9
    ? fun.func(a, b, c, d, e, f, g, h, i)
    : fun(a)(b)(c)(d)(e)(f)(g)(h)(i);
}

//// LIST STUFF ////

var Nil = { ctor: '[]' };

function Cons(hd, tl)
{
	return {
		ctor: '::',
		_0: hd,
		_1: tl
	};
}

function append(xs, ys)
{
	// append Strings
	if (typeof xs === 'string')
	{
		return xs + ys;
	}

	// append Lists
	if (xs.ctor === '[]')
	{
		return ys;
	}
	var root = Cons(xs._0, Nil);
	var curr = root;
	xs = xs._1;
	while (xs.ctor !== '[]')
	{
		curr._1 = Cons(xs._0, Nil);
		xs = xs._1;
		curr = curr._1;
	}
	curr._1 = ys;
	return root;
}

Elm, TypeScript and other JavaScript transpilers adds quite overhead to every function call (however I feel that every transpiler result suits good for JIT compiler).

Flow seems better for me: we still have raw (high-speed) JavaScript code, but we get a powerful type checking.

Also we need to try Rust-to-wasm approach. It looks ultimate, but I have no idea about its performance.

@pepyakin
Copy link
Contributor

pepyakin commented Jul 20, 2017

Just came here to mention rust-to-wasm, but it is already here. 😆

So, I decided to publish my experiments with Rust-WebAssembly approach here: it is stripped duct-taped version of exonum core. may be it will help to start assessing this approach.

Good thing about this approach is that one can share it's types and message definitions (for example).
Bad thing is that Rust and WebAssembly story isn't complete yet.

Anyway, I believe that Rust-WebAssembly approach is worth exploring.

@stanislav-tkach
Copy link
Contributor

I suppose it is worth creating a separate issue about WebAssembly.

@pepyakin
Copy link
Contributor

@DarkEld3r Do you mean here or in exonum-core?

@stanislav-tkach
Copy link
Contributor

@pepyakin I suppose it is more related to the "exonum-client".

@vnermolaev
Copy link

vnermolaev commented Oct 17, 2017

@deniskolodin I do not think it's correct to compare Elm and Typescript (or Flow). While the former is a full-blown language targeting web that gets transpiled to JS (much like, say, Dart), the latter performs static type checks. TS leaves no trace of itself after transpilation producing a pure JS code free of any run-time type checks.

I personally favor TS mainly due to its design goals and the fact that TS lives up to them.

And from what I see in the present exonum-client you already write something very similar to TS but employing jsdoc annotations. In case of TS they would have to be moved into definitions of the functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

5 participants