Skip to content

Instantly share code, notes, and snippets.

@patrickdlogan
Last active April 27, 2020 23:34
Show Gist options
  • Save patrickdlogan/444378f56345a66501d8f38c31e75bcc to your computer and use it in GitHub Desktop.
Save patrickdlogan/444378f56345a66501d8f38c31e75bcc to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
import 'dart:collection';
void foo() {
Variable fahrenheit;
final celsius =
Variable.formula(() => ((fahrenheit.get() as double) - 32.0) / 1.8, 0.0);
fahrenheit =
Variable.formula(() => ((celsius.get() as double) * 1.8) + 32.0, 32.0);
print(celsius.get() == 0.0);
print(fahrenheit.get() == 32.0);
celsius.set(100.0);
print(fahrenheit.get() == 212.0);
print(celsius.get() == 100.0);
fahrenheit.set(-459.67);
print(fahrenheit.get() == -459.67);
print(celsius.get() == -273.15);
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Temperature Conversion',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Temperature Conversion Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Variable fahrenheit;
Variable celsius;
_MyHomePageState() {
fahrenheit =
Variable.formula(() => ((celsius.get() as double) * 1.8) + 32.0, 32.0);
celsius = Variable.formula(
() => ((fahrenheit.get() as double) - 32.0) / 1.8, 0.0);
}
void _incrementCounter() {
setState(() {
celsius.set((celsius.get() as int) + 1);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Celsius:',
),
Text(
'${celsius.get()}',
style: Theme.of(context).textTheme.display1,
),
Text(
'Fahrenheit:',
),
Text(
'${(fahrenheit.get() as num).toStringAsFixed(2)}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class _ConstraintSystem {
static final _ConstraintSystem _instance = _ConstraintSystem();
final _demandingVariables = ListQueue<_VariableImpl>();
factory _ConstraintSystem.singleton() => _instance;
_ConstraintSystem();
}
class _Dependency {
final _VariableImpl _variable;
int _timestamp = 0;
_Dependency(this._variable) {
_timestamp = this._variable._timestamp + 1;
}
}
typedef Formula = Object Function();
abstract class Variable {
Variable();
factory Variable.value(Object value) => _VariableImpl.value(value);
factory Variable.formula(Formula formula, Object value) =>
_VariableImpl.formula(formula, value);
/// Get the value of the variable. Out-of-date formulas are updated if necessary.
Object get();
void set(Object value);
Type get valueType;
int get length => 1;
}
/// A variable that can maintain a value that depends on other variables including cyclic dependencies.
class _VariableImpl extends Variable {
Object _value;
Formula _formula;
int _timestamp = 0;
bool _outOfDate = false;
final _dependents = <_Dependency>[];
_VariableImpl() {
_value = null;
}
/// Construct a Variable whose value is explicit and is not a formula that depends on any other Variable.
_VariableImpl.value(this._value);
// Construct a Variable whose value is computed by a formula, is initially out of date, with cyclical dependencies initially resolved by the given value.
_VariableImpl.formula(this._formula, this._value) {
_outOfDate = true;
}
/// Get the value of the variable, computing the formula if it is out of date, with cyclical dependencies resolved using the current value.
@override
Object get() {
final system = _ConstraintSystem.singleton();
if (system._demandingVariables.isNotEmpty) {
final demandingVariable = system._demandingVariables.first;
final dep = _dependents.firstWhere(
(dep) => dep._variable == demandingVariable,
orElse: () => null);
if (dep == null) {
_dependents.add(_Dependency(demandingVariable));
} else {
dep._timestamp = demandingVariable._timestamp + 1;
}
}
if (_outOfDate) {
system._demandingVariables.addFirst(this);
_outOfDate = false;
_value = _formula();
_timestamp++;
system._demandingVariables.removeFirst();
}
return _value;
}
/// Assign to the variable an explicit value that is not a formula, marking any dependents out of date.
@override
void set(Object value) {
_value = value;
_mark(<Variable>[]);
// Explicit values are not computed and so are never out of date.
_outOfDate = false;
}
/// Mark the variable and all dependents of the variable out of date, placing the dependents in a queue.
void _mark(List<Variable> marked) {
if (marked.contains(this)) return;
marked.add(this);
_outOfDate =
true; // A Variable associated with a stateful Flutter widget would set the state changed here.
final removals = <_Dependency>[];
_dependents.forEach((dep) {
if (dep._timestamp < dep._variable._timestamp) {
removals.add(dep);
} else if (!dep._variable._outOfDate) {
dep._variable._mark(marked);
}
});
marked.remove(this);
removals.forEach((dep) {
_dependents.remove(dep);
});
}
@override
Type get valueType => (_outOfDate) ? Formula : _value.runtimeType;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment