SlideShare a Scribd company logo
1 of 49
Download to read offline
from Madness to Reason
@romanschejbal
from React to ReasonReact
SYNTAX & TOOLCHAIN FOR OCAML
BATTLE-TESTED PROGRAMMING LANGUAGE
Bucklescript
https://github.com/facebook/reason/blob/master/src/README.md
OCaml?
Why should I care? 🤔
Effective & Functional
✔
But Still Practical
Statically Type Checked
✔
Compiler Errors
We've found a bug for you!
/workspace/reason/src/Student.re 20:30
19 │ let age = 2;
20 │ let greeting = greet(Student(1));
21 │
22 │ Js.log(greeting);
This has type:
int
But somewhere wanted:
string
You can convert a int to a string with string_of_int.
Good Support For
Immutable Programming
✔
Automatic Type Inference
(less type definitions)
Autocurrying
the “Madness”
@romanschejbal
let name = "Reason";
let age = 2;
let greet = () => "Hello World!";
Language Basics
Algebraic data types
type name = option(string); /* None | Some(“Reason”) */
type state = { items: array(string), length: number };
type schoolPerson =
| Teacher
| Director
| Student(string);
type schoolPerson = Teacher | Director | Student(string);
let greet = stranger =>
switch (stranger) {
| Teacher => "Hey professor!"
| Director => "Hello director."
| Student("Richard") => "Still here Ricky?"
| Student(anyOtherName) => "Hey, " ++ anyOtherName ++ "."
};
greet(Teacher); /* Hey professor! */
greet(Student("OCaml")); /* Hey, OCaml. */
Variants & Pattern Matching
Pattern Matching Exhaustiveness
Warning number 8
/workspace/reason/src/Student.re 7:3-12:3
5 │
6 │ let greet = stranger =>
7 │ switch (stranger) {
8 │ | Teacher => "Hey professor!"
. │ ...
11 │ /* | Student(anyOtherName) => "Hey " ++ anyOtherName ++ "." */
12 │ };
13 │
14 │ print_endline(greet(Teacher));
You forgot to handle a possible value here, for example:
Student ""
type userT = option(string);
let userOne = Some("X");
let userTwo = None;
let printPlayerName = player =>
switch (player) {
| Some(name) => "Logged in as " ++ name
| None => "Not logged in"
};
Option type
Why Reason then? 🤔
“We want people to be
able to use powerful, well-
typed languages at work,
not just in their free time.”
- Jordan Walke
https://jaredforsyth.com/type-safe-react-native/
How Do I Start 🧐
CRA & Redux
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | |- store.js
| |- App.js
| |- index.js
| package.json
| bsconfig.json
| yarn.lock
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | |- store.js
| |- App.js
| |- index.js
| package.json
| bsconfig.json
| yarn.lock
yarn add bs-platform reason-react
bsconfig.json
{
"name": "my-project",
"namespace": true,
"sources": [
{
"dir": "src",
"subdirs": true
}
],
"package-specs": {
"module": "es6-global",
"in-source": true
},
"suffix": ".bs.js",
"bs-dependencies": ["reason-react"],
"reason": {
"react-jsx": 2
}
}
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | |- store.js
| |- App.js
| |- index.js
| package.json
| bsconfig.json
| yarn.lock
package.json
{
"name": "from-react-to-reason",
"version": "0.1.0",
"private": true,
"dependencies": {
"bs-platform": "^3.0.0",
"flow-bin": "^0.72.0",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-redux": "^5.0.7",
"react-scripts": "1.1.4",
"reason-react": "^0.4.1",
"redux": "^4.0.0",
"redux-thunk": "^2.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"bsb": "bsb -make-world -w"
}
}
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | |- store.js
| |- App.js
| |- index.js
| package.json
| bsconfig.json
| yarn.lock
"bsb": "bsb -make-world -w"
src/Types.re
[@bs.deriving jsConverter]
type flightInfo = {
airline: string,
price: int,
duration: float,
};
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
Record Type
src/Types.bs.js
// Generated by BUCKLESCRIPT VERSION 3.0.0, PLEASE EDIT WITH CARE
function flightInfoToJs(param) {
return {
airline: param[ /* airline */0],
price: param[ /* price */1],
duration: param[ /* duration */2]
};
}
function flightInfoFromJs(param) {
return /* record */[
/* airline */param.airline,
/* price */param.price,
/* duration */param.duration
];
}
export {
flightInfoToJs ,
flightInfoFromJs ,
}
/* No side effect */
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
src/state/reducer/index.js
import {
type actionT,
REQUEST_FLIGHTS,
RECEIVE_FLIGHTS,
FAILURE_FLIGHTS,
} from ' ../action';
const initialState = {
loading: false,
flights: [],
error: null,
};
export default (state = initialState, action) => {
switch (action.type) {
case REQUEST_FLIGHTS:
return { ...state, loading: true };
case RECEIVE_FLIGHTS:
return { ...state, loading: false, flights: action.payload.flights };
case FAILURE_FLIGHTS:
return { ...state, error: action.payload.error };
default:
return state;
}
};
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
src/state/reducer/Reducer.re
type flightsData = array(Types.flightInfo);
[@bs.deriving jsConverter]
type state = {
loading: bool,
flights: flightsData,
error: option(Js.Exn.t)
};
let initialState = {
loading: false,
flights: [ ||],
error: None
};
let default = (state, action) =>
switch (action ##_type) {
| "REQUEST_FLIGHTS" => { ...state, loading: true}
| "RECEIVE_FLIGHTS" => {
...state,
flights: Array.map(Types.flightInfoFromJs, action ##payload ##flights),
loading: false,
}
| "FAILURE_FLIGHTS" => { ...state, error: action ##payload ##error}
};
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
src/state/reducer/Reducer.re
Warning number 8
/Users/roman/workspace/from-react-to-reason/src/state/reducer/Reducer.re 21:3-30:3
19 │
20 │ let default = (state, action) =>
21 │ switch (action##_type) {
22 │ | "REQUEST_FLIGHTS" => {...state, loading: true}
. │ ...
29 │ | “FAILURE_FLIGHTS" => {...state, error: action##payload##error}
30 │ };
31 │
32 │ /* let default = (state, action) => {
You forgot to handle a possible value here, for example:
""
src/state/reducer/Reducer.re
type flightsData = array(Types.flightInfo);
type state = {
loading: bool,
flights: flightsData,
error: option(Js.Exn.t)
};
let initialState = {
loading: false,
flights: [ ||],
error: None
};
let default = (state, action) =>
switch (action ##_type) {
| "REQUEST_FLIGHTS" => { ...state, loading: true}
| "RECEIVE_FLIGHTS" => {
...state,
flights: Array.map(Types.flightInfoFromJs, action ##payload ##flights),
loading: false,
}
| "FAILURE_FLIGHTS" => { ...state, error: action ##payload ##error}
| _ => state
};
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
| _ => state
src/state/action/index.js
export const REQUEST_FLIGHTS = 'REQUEST_FLIGHTS';
export const RECEIVE_FLIGHTS = 'RECEIVE_FLIGHTS';
export const FAILURE_FLIGHTS = 'FAILURE_FLIGHTS';
export const fetchFlights = (where, when) => (
dispatch
) => {
dispatch(requestFlights());
setTimeout(
() =>
dispatch(
receiveFlights([
{ airline: 'British Airways', price: 390, duration: 3.5 },
{ airline: 'KLM', price: 340, duration: 2.2 }
])
),
3000
);
};
const requestFlights = () => ({
type: REQUEST_FLIGHTS
});
const receiveFlights = (flights: Array<Object>) => ({
type: RECEIVE_FLIGHTS,
payload: { flights }
});
const failureFlights = (error: Error) => ({
type: FAILURE_FLIGHTS,
payload: { error }
});
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
src/state/action/Actions.re
let fetchFlights = (from, where, when) =>
(. dispatch) => {
dispatch(requestFlights(from, where, when));
Js.Global.setTimeout(
() =>
dispatch(
receiveFlights( [|
{airline: "British Airways", price: 390, duration: 3.5},
{airline: "KLM", price: 340, duration: 2.2},
|]),
),
3000,
);
};
let requestFlights = (from, where, when) => {
"type": "REQUEST_FLIGHTS",
"payload": {
"from": from,
"where": where,
"when": when,
},
};
let receiveFlights = flights => {
"type": "RECEIVE_FLIGHTS",
"payload": {
"flights": flights,
},
};
...
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | | |- Actions.re
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
src/state/store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducer/Reducer.bs';
const initialState = undefined;
export default () =>
createStore(
rootReducer,
applyMiddleware(thunk),
initialState
);
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | | |- Actions.re
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
What have we gained?
1. Can’t mess up our state shape
Can we get more?
src/state/action/Actions.re
let fetchFlights = (from, where, when) =>
(. dispatch) => {
dispatch(requestFlights(from, where, when));
Js.Global.setTimeout(
() =>
dispatch(
receiveFlights( [|
{airline: "British Airways", price: 390, duration: 3.5},
{airline: "KLM", price: 340, duration: 2.2},
|]),
),
3000,
);
};
let requestFlights = (from, where, when) => {
"type": "REQUEST_FLIGHTS",
"payload": {
"from": from,
"where": where,
"when": when,
},
};
let receiveFlights = flights => {
"type": "RECEIVE_FLIGHTS",
"payload": {
"flights": flights,
},
};
let failureFlights = error => {
"type": "FAILURE_FLIGHTS",
"payload": {
"error": error,
},
};
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | | |- Actions.re
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
src/state/action/Actions.re
type from = string;
type where = string;
type when = string;
[@bs.deriving accessors]
type action =
| RequestFlights(from, where, when)
| ReceiveFlights(array(Types.flightInfo))
| FailureFlights(Js.Exn.t);
let fetchFlights = (from, where, when) =>
(. dispatch) => {
dispatch(RequestFlights(from, where, when));
Js.Global.setTimeout(
() =>
dispatch(
ReceiveFlights( [|
{airline: "British Airways", price: 390, duration: 3.5},
{airline: "KLM", price: 340, duration: 2.2},
|]),
),
3000,
);
};
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | | |- Actions.re
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
src/state/action/Reducer.re
type flightsData = array(Types.flightInfo);
[@bs.deriving jsConverter]
type state = {
loading: bool,
flights: flightsData,
error: option(Js.Exn.t),
};
let initialState = {
loading: false,
flights: [ ||],
error: None,
};
let default = (state, action) => {
Actions.(
switch (action) {
| RequestFlights(_, _, _) => { ...state, loading: true}
| ReceiveFlights(flights) => { ...state, flights}
| FailureFlights(error) => { ...state, error: Some(error)}
}
)
};
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | | |- Actions.re
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
src/state/store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducer/Reducer.bs';
const fromReasonToJs = store => next => action => {
if (action.tag !== undefined) {
// reason action
const { tag } = action;
action = {
type: `REASON_ACTION_${tag}`,
tag,
reasonAction: action
};
}
next(action);
};
const reasonReducer = reducer => (state, action) => {
if (action.reasonAction) return reducer(state, action.reasonAction);
return reducer(state, { ...action, tag: -1 });
};
const initialState = undefined;
export default () =>
createStore(
reasonReducer(rootReducer),
applyMiddleware(thunk, fromReasonToJs),
initialState
);
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| |- state/
| | |- action/
| | | |- index.js
| | | |- Actions.re
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
src/components/FlightList.re
open ReasonReact;
let component = statelessComponent("FlightsList");
let make = (~data, _children) => {
...component,
render: _self =>
<div>
(
Array.map(
flightInfo =>
<div>
<h2> (string(flightInfo.airline)) </h2>
<p>
(string("Price: $" ++ string_of_int(flightInfo.price)))
(
string(
" | Duration: "
++ string_of_float(flightInfo.duration)
++ "hours",
)
)
</p>
</div>,
data,
)
|> array
)
</div>,
};
let default =
wrapReasonForJs(~component, jsProps =>
make(~data=jsProps ##data, jsProps ##children)
);
flight-search/
|- public/
|- node_modules/
|- public/
|- src/
| |- components/
| | |- flight-list.js
| | | |- FlightList.js
| |- state/
| | |- action/
| | | |- index.js
| | | |- Actions.re
| | |- reducer/
| | | |- index.js
| | | |- Reducer.re
| | |- store.js
| |- App.js
| |- index.js
| |- Types.re
| package.json
| bsconfig.json
| yarn.lock
What have we gained?
1. Can’t mess up our state shape
2. Can’t forget to handle actions
3. Can’t forget to handle edge cases
Not covered…
• Rendering a JS component from a Reason component
• Using Variants for async stuff i.e.
[@bs.module]
external myJSComponent : ReasonReact.reactClass = “./my-js-component“;
let make = (~name: string, ~age: option(int)=?, children) =>
ReasonReact.wrapJsForReason(
~reactClass=myJSComponent,
~props={"name": name, "age": Js.Nullable.fromOption(age)},
children,
);
type async('a) =
| Idle
| Loading
| Success('a)
| Error(Js.Exn.t);
type action =
| Tick(unit)
| Flights(Types.(async(array(flightInfo))));
http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipattern.html
THANKS!
@romanschejbal
github.com/romanschejbal/from-react-to-reason

More Related Content

What's hot

Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram VaswaniCreating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswanivvaswani
 
AngularJS with Slim PHP Micro Framework
AngularJS with Slim PHP Micro FrameworkAngularJS with Slim PHP Micro Framework
AngularJS with Slim PHP Micro FrameworkBackand Cohen
 
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)James Titcumb
 
Keep It Simple Security (Symfony cafe 28-01-2016)
Keep It Simple Security (Symfony cafe 28-01-2016)Keep It Simple Security (Symfony cafe 28-01-2016)
Keep It Simple Security (Symfony cafe 28-01-2016)Oleg Zinchenko
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkJeremy Kendall
 
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...Caldera Labs
 
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложенияCodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложенияCodeFest
 
Getting into ember.js
Getting into ember.jsGetting into ember.js
Getting into ember.jsreybango
 
Introduction to AngularJS For WordPress Developers
Introduction to AngularJS For WordPress DevelopersIntroduction to AngularJS For WordPress Developers
Introduction to AngularJS For WordPress DevelopersCaldera Labs
 
Workshop 24: React Native Introduction
Workshop 24: React Native IntroductionWorkshop 24: React Native Introduction
Workshop 24: React Native IntroductionVisual Engineering
 
How to driver your webservices with ansible
How to driver your webservices with ansibleHow to driver your webservices with ansible
How to driver your webservices with ansibleGonéri Le Bouder
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportBen Scofield
 
Turn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modulesTurn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modulesjerryorr
 

What's hot (18)

Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram VaswaniCreating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
 
AngularJS with Slim PHP Micro Framework
AngularJS with Slim PHP Micro FrameworkAngularJS with Slim PHP Micro Framework
AngularJS with Slim PHP Micro Framework
 
DrupalCon jQuery
DrupalCon jQueryDrupalCon jQuery
DrupalCon jQuery
 
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
 
Workshop 6: Designer tools
Workshop 6: Designer toolsWorkshop 6: Designer tools
Workshop 6: Designer tools
 
Oro meetup #4
Oro meetup #4Oro meetup #4
Oro meetup #4
 
Keep It Simple Security (Symfony cafe 28-01-2016)
Keep It Simple Security (Symfony cafe 28-01-2016)Keep It Simple Security (Symfony cafe 28-01-2016)
Keep It Simple Security (Symfony cafe 28-01-2016)
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro framework
 
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
 
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложенияCodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
CodeFest 2014. Пухальский И. — Отзывчивые кроссплатформенные веб-приложения
 
Getting into ember.js
Getting into ember.jsGetting into ember.js
Getting into ember.js
 
Introduction to AngularJS For WordPress Developers
Introduction to AngularJS For WordPress DevelopersIntroduction to AngularJS For WordPress Developers
Introduction to AngularJS For WordPress Developers
 
Workshop 24: React Native Introduction
Workshop 24: React Native IntroductionWorkshop 24: React Native Introduction
Workshop 24: React Native Introduction
 
GHC
GHCGHC
GHC
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
How to driver your webservices with ansible
How to driver your webservices with ansibleHow to driver your webservices with ansible
How to driver your webservices with ansible
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack Support
 
Turn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modulesTurn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modules
 

Similar to Roman Schejbal: From Madness To Reason

Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsFrancois Zaninotto
 
JavaScript on Rails 튜토리얼
JavaScript on Rails 튜토리얼JavaScript on Rails 튜토리얼
JavaScript on Rails 튜토리얼Sukjoon Kim
 
Dependency Management with RequireJS
Dependency Management with RequireJSDependency Management with RequireJS
Dependency Management with RequireJSAaronius
 
GHC Participant Training
GHC Participant TrainingGHC Participant Training
GHC Participant TrainingAidIQ
 
JavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best PracticesJavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best PracticesSiarhei Barysiuk
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js frameworkBen Lin
 
Java Airline Reservation System – Travel Smarter, Not Harder.pdf
Java Airline Reservation System – Travel Smarter, Not Harder.pdfJava Airline Reservation System – Travel Smarter, Not Harder.pdf
Java Airline Reservation System – Travel Smarter, Not Harder.pdfSudhanshiBakre1
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPresswpnepal
 
Security Challenges in Node.js
Security Challenges in Node.jsSecurity Challenges in Node.js
Security Challenges in Node.jsWebsecurify
 
Types End-to-End @ samsara
Types End-to-End @ samsaraTypes End-to-End @ samsara
Types End-to-End @ samsaraStephen Wan
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Michelangelo van Dam
 
PHP: GraphQL consistency through code generation
PHP: GraphQL consistency through code generationPHP: GraphQL consistency through code generation
PHP: GraphQL consistency through code generationAlexander Obukhov
 
Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Arthur Puthin
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda Katz
 
Chaincode Development 區塊鏈鏈碼開發
Chaincode Development 區塊鏈鏈碼開發Chaincode Development 區塊鏈鏈碼開發
Chaincode Development 區塊鏈鏈碼開發HO-HSUN LIN
 

Similar to Roman Schejbal: From Madness To Reason (20)

Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
JavaScript on Rails 튜토리얼
JavaScript on Rails 튜토리얼JavaScript on Rails 튜토리얼
JavaScript on Rails 튜토리얼
 
Dependency Management with RequireJS
Dependency Management with RequireJSDependency Management with RequireJS
Dependency Management with RequireJS
 
GHC Participant Training
GHC Participant TrainingGHC Participant Training
GHC Participant Training
 
JavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best PracticesJavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best Practices
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
 
Reduxing like a pro
Reduxing like a proReduxing like a pro
Reduxing like a pro
 
JavaScript Core
JavaScript CoreJavaScript Core
JavaScript Core
 
Java Airline Reservation System – Travel Smarter, Not Harder.pdf
Java Airline Reservation System – Travel Smarter, Not Harder.pdfJava Airline Reservation System – Travel Smarter, Not Harder.pdf
Java Airline Reservation System – Travel Smarter, Not Harder.pdf
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
 
Postman On Steroids
Postman On SteroidsPostman On Steroids
Postman On Steroids
 
20150319 testotipsio
20150319 testotipsio20150319 testotipsio
20150319 testotipsio
 
Security Challenges in Node.js
Security Challenges in Node.jsSecurity Challenges in Node.js
Security Challenges in Node.js
 
Types End-to-End @ samsara
Types End-to-End @ samsaraTypes End-to-End @ samsara
Types End-to-End @ samsara
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
 
PHP: GraphQL consistency through code generation
PHP: GraphQL consistency through code generationPHP: GraphQL consistency through code generation
PHP: GraphQL consistency through code generation
 
Spring boot
Spring boot Spring boot
Spring boot
 
Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Chaincode Development 區塊鏈鏈碼開發
Chaincode Development 區塊鏈鏈碼開發Chaincode Development 區塊鏈鏈碼開發
Chaincode Development 區塊鏈鏈碼開發
 

More from Develcz

Daniel Steigerwald: WYSIWYG je šik! (když funguje)
Daniel Steigerwald: WYSIWYG je šik! (když funguje)Daniel Steigerwald: WYSIWYG je šik! (když funguje)
Daniel Steigerwald: WYSIWYG je šik! (když funguje)Develcz
 
Ondřej Hlaváček: Životní cyklus featury
Ondřej Hlaváček: Životní cyklus featuryOndřej Hlaváček: Životní cyklus featury
Ondřej Hlaváček: Životní cyklus featuryDevelcz
 
Martin Hassman: Jak se tvoří addony pro World of Warcraft
Martin Hassman: Jak se tvoří addony pro World of WarcraftMartin Hassman: Jak se tvoří addony pro World of Warcraft
Martin Hassman: Jak se tvoří addony pro World of WarcraftDevelcz
 
Ondřej Voves: Jak přepsat monolit do mikroslužeb
Ondřej Voves: Jak přepsat monolit do mikroslužebOndřej Voves: Jak přepsat monolit do mikroslužeb
Ondřej Voves: Jak přepsat monolit do mikroslužebDevelcz
 
Marcel Šulek: Zpraste svoje kódy
Marcel Šulek: Zpraste svoje kódyMarcel Šulek: Zpraste svoje kódy
Marcel Šulek: Zpraste svoje kódyDevelcz
 
Michal Illich: Vývojáři staví letadlo
Michal Illich: Vývojáři staví letadloMichal Illich: Vývojáři staví letadlo
Michal Illich: Vývojáři staví letadloDevelcz
 
Ondřej Kokeš: Zpracování dat z veřejných zdrojů
Ondřej Kokeš: Zpracování dat z veřejných zdrojůOndřej Kokeš: Zpracování dat z veřejných zdrojů
Ondřej Kokeš: Zpracování dat z veřejných zdrojůDevelcz
 
Patrick Zandl: Open source software, hardware, 3D tiskárny a tvrdý business -...
Patrick Zandl: Open source software, hardware, 3D tiskárny a tvrdý business -...Patrick Zandl: Open source software, hardware, 3D tiskárny a tvrdý business -...
Patrick Zandl: Open source software, hardware, 3D tiskárny a tvrdý business -...Develcz
 
Štěpán Bechynský: Hardware pro IoT projekty nejen pro hobby, ale i pro průmysl
Štěpán Bechynský: Hardware pro IoT projekty nejen pro hobby, ale i pro průmyslŠtěpán Bechynský: Hardware pro IoT projekty nejen pro hobby, ale i pro průmysl
Štěpán Bechynský: Hardware pro IoT projekty nejen pro hobby, ale i pro průmyslDevelcz
 
Tomáš Vondra: Paralelizace dotazu a partitioning v PostgreSQL
Tomáš Vondra: Paralelizace dotazu a partitioning v PostgreSQLTomáš Vondra: Paralelizace dotazu a partitioning v PostgreSQL
Tomáš Vondra: Paralelizace dotazu a partitioning v PostgreSQLDevelcz
 
Tomáš Zvěřina: Flutter.io - multiplatformní vývoj mobilních aplikací
Tomáš Zvěřina: Flutter.io - multiplatformní vývoj mobilních aplikacíTomáš Zvěřina: Flutter.io - multiplatformní vývoj mobilních aplikací
Tomáš Zvěřina: Flutter.io - multiplatformní vývoj mobilních aplikacíDevelcz
 
Jakub Vrána: Dokazatelná bezpečnost
Jakub Vrána: Dokazatelná bezpečnostJakub Vrána: Dokazatelná bezpečnost
Jakub Vrána: Dokazatelná bezpečnostDevelcz
 
Michal Illich: Zuri aneb Vývojáři staví letadlo
Michal Illich: Zuri aneb Vývojáři staví letadloMichal Illich: Zuri aneb Vývojáři staví letadlo
Michal Illich: Zuri aneb Vývojáři staví letadloDevelcz
 
Ondřej Šika: Docker, Traefik a CI - Mějte nasazené všeny větve na kterých pra...
Ondřej Šika: Docker, Traefik a CI - Mějte nasazené všeny větve na kterých pra...Ondřej Šika: Docker, Traefik a CI - Mějte nasazené všeny větve na kterých pra...
Ondřej Šika: Docker, Traefik a CI - Mějte nasazené všeny větve na kterých pra...Develcz
 
David Majda: Autoformátování kódu
David Majda: Autoformátování kóduDavid Majda: Autoformátování kódu
David Majda: Autoformátování kóduDevelcz
 
David Grudl: Open source: The Good, the Bad and the Ugly
David Grudl: Open source: The Good, the Bad and the UglyDavid Grudl: Open source: The Good, the Bad and the Ugly
David Grudl: Open source: The Good, the Bad and the UglyDevelcz
 
Ondřej Machulda: Začíná zlatá doba end-to-end testů!
Ondřej Machulda: Začíná zlatá doba end-to-end testů!Ondřej Machulda: Začíná zlatá doba end-to-end testů!
Ondřej Machulda: Začíná zlatá doba end-to-end testů!Develcz
 
Adam Kudrna: Headless WordPress/Drupal
Adam Kudrna: Headless WordPress/DrupalAdam Kudrna: Headless WordPress/Drupal
Adam Kudrna: Headless WordPress/DrupalDevelcz
 
Jaroslav Tulach: GraalVM - z vývoje nejrychlejšího virtuálního stroje na světě
Jaroslav Tulach: GraalVM - z vývoje nejrychlejšího virtuálního stroje na světěJaroslav Tulach: GraalVM - z vývoje nejrychlejšího virtuálního stroje na světě
Jaroslav Tulach: GraalVM - z vývoje nejrychlejšího virtuálního stroje na světěDevelcz
 
Martin Michálek: Bootstrap 4 - Jednou to muselo přijít
Martin Michálek: Bootstrap 4 - Jednou to muselo přijítMartin Michálek: Bootstrap 4 - Jednou to muselo přijít
Martin Michálek: Bootstrap 4 - Jednou to muselo přijítDevelcz
 

More from Develcz (20)

Daniel Steigerwald: WYSIWYG je šik! (když funguje)
Daniel Steigerwald: WYSIWYG je šik! (když funguje)Daniel Steigerwald: WYSIWYG je šik! (když funguje)
Daniel Steigerwald: WYSIWYG je šik! (když funguje)
 
Ondřej Hlaváček: Životní cyklus featury
Ondřej Hlaváček: Životní cyklus featuryOndřej Hlaváček: Životní cyklus featury
Ondřej Hlaváček: Životní cyklus featury
 
Martin Hassman: Jak se tvoří addony pro World of Warcraft
Martin Hassman: Jak se tvoří addony pro World of WarcraftMartin Hassman: Jak se tvoří addony pro World of Warcraft
Martin Hassman: Jak se tvoří addony pro World of Warcraft
 
Ondřej Voves: Jak přepsat monolit do mikroslužeb
Ondřej Voves: Jak přepsat monolit do mikroslužebOndřej Voves: Jak přepsat monolit do mikroslužeb
Ondřej Voves: Jak přepsat monolit do mikroslužeb
 
Marcel Šulek: Zpraste svoje kódy
Marcel Šulek: Zpraste svoje kódyMarcel Šulek: Zpraste svoje kódy
Marcel Šulek: Zpraste svoje kódy
 
Michal Illich: Vývojáři staví letadlo
Michal Illich: Vývojáři staví letadloMichal Illich: Vývojáři staví letadlo
Michal Illich: Vývojáři staví letadlo
 
Ondřej Kokeš: Zpracování dat z veřejných zdrojů
Ondřej Kokeš: Zpracování dat z veřejných zdrojůOndřej Kokeš: Zpracování dat z veřejných zdrojů
Ondřej Kokeš: Zpracování dat z veřejných zdrojů
 
Patrick Zandl: Open source software, hardware, 3D tiskárny a tvrdý business -...
Patrick Zandl: Open source software, hardware, 3D tiskárny a tvrdý business -...Patrick Zandl: Open source software, hardware, 3D tiskárny a tvrdý business -...
Patrick Zandl: Open source software, hardware, 3D tiskárny a tvrdý business -...
 
Štěpán Bechynský: Hardware pro IoT projekty nejen pro hobby, ale i pro průmysl
Štěpán Bechynský: Hardware pro IoT projekty nejen pro hobby, ale i pro průmyslŠtěpán Bechynský: Hardware pro IoT projekty nejen pro hobby, ale i pro průmysl
Štěpán Bechynský: Hardware pro IoT projekty nejen pro hobby, ale i pro průmysl
 
Tomáš Vondra: Paralelizace dotazu a partitioning v PostgreSQL
Tomáš Vondra: Paralelizace dotazu a partitioning v PostgreSQLTomáš Vondra: Paralelizace dotazu a partitioning v PostgreSQL
Tomáš Vondra: Paralelizace dotazu a partitioning v PostgreSQL
 
Tomáš Zvěřina: Flutter.io - multiplatformní vývoj mobilních aplikací
Tomáš Zvěřina: Flutter.io - multiplatformní vývoj mobilních aplikacíTomáš Zvěřina: Flutter.io - multiplatformní vývoj mobilních aplikací
Tomáš Zvěřina: Flutter.io - multiplatformní vývoj mobilních aplikací
 
Jakub Vrána: Dokazatelná bezpečnost
Jakub Vrána: Dokazatelná bezpečnostJakub Vrána: Dokazatelná bezpečnost
Jakub Vrána: Dokazatelná bezpečnost
 
Michal Illich: Zuri aneb Vývojáři staví letadlo
Michal Illich: Zuri aneb Vývojáři staví letadloMichal Illich: Zuri aneb Vývojáři staví letadlo
Michal Illich: Zuri aneb Vývojáři staví letadlo
 
Ondřej Šika: Docker, Traefik a CI - Mějte nasazené všeny větve na kterých pra...
Ondřej Šika: Docker, Traefik a CI - Mějte nasazené všeny větve na kterých pra...Ondřej Šika: Docker, Traefik a CI - Mějte nasazené všeny větve na kterých pra...
Ondřej Šika: Docker, Traefik a CI - Mějte nasazené všeny větve na kterých pra...
 
David Majda: Autoformátování kódu
David Majda: Autoformátování kóduDavid Majda: Autoformátování kódu
David Majda: Autoformátování kódu
 
David Grudl: Open source: The Good, the Bad and the Ugly
David Grudl: Open source: The Good, the Bad and the UglyDavid Grudl: Open source: The Good, the Bad and the Ugly
David Grudl: Open source: The Good, the Bad and the Ugly
 
Ondřej Machulda: Začíná zlatá doba end-to-end testů!
Ondřej Machulda: Začíná zlatá doba end-to-end testů!Ondřej Machulda: Začíná zlatá doba end-to-end testů!
Ondřej Machulda: Začíná zlatá doba end-to-end testů!
 
Adam Kudrna: Headless WordPress/Drupal
Adam Kudrna: Headless WordPress/DrupalAdam Kudrna: Headless WordPress/Drupal
Adam Kudrna: Headless WordPress/Drupal
 
Jaroslav Tulach: GraalVM - z vývoje nejrychlejšího virtuálního stroje na světě
Jaroslav Tulach: GraalVM - z vývoje nejrychlejšího virtuálního stroje na světěJaroslav Tulach: GraalVM - z vývoje nejrychlejšího virtuálního stroje na světě
Jaroslav Tulach: GraalVM - z vývoje nejrychlejšího virtuálního stroje na světě
 
Martin Michálek: Bootstrap 4 - Jednou to muselo přijít
Martin Michálek: Bootstrap 4 - Jednou to muselo přijítMartin Michálek: Bootstrap 4 - Jednou to muselo přijít
Martin Michálek: Bootstrap 4 - Jednou to muselo přijít
 

Recently uploaded

TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park masabamasaba
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfonteinmasabamasaba
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfVishalKumarJha10
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfryanfarris8
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is insideshinachiaurasa2
 
Pharm-D Biostatistics and Research methodology
Pharm-D Biostatistics and Research methodologyPharm-D Biostatistics and Research methodology
Pharm-D Biostatistics and Research methodologyAnusha Are
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisamasabamasaba
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionOnePlan Solutions
 
LEVEL 5 - SESSION 1 2023 (1).pptx - PDF 123456
LEVEL 5   - SESSION 1 2023 (1).pptx - PDF 123456LEVEL 5   - SESSION 1 2023 (1).pptx - PDF 123456
LEVEL 5 - SESSION 1 2023 (1).pptx - PDF 123456KiaraTiradoMicha
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnAmarnathKambale
 

Recently uploaded (20)

TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
Pharm-D Biostatistics and Research methodology
Pharm-D Biostatistics and Research methodologyPharm-D Biostatistics and Research methodology
Pharm-D Biostatistics and Research methodology
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
LEVEL 5 - SESSION 1 2023 (1).pptx - PDF 123456
LEVEL 5   - SESSION 1 2023 (1).pptx - PDF 123456LEVEL 5   - SESSION 1 2023 (1).pptx - PDF 123456
LEVEL 5 - SESSION 1 2023 (1).pptx - PDF 123456
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 

Roman Schejbal: From Madness To Reason

  • 1. from Madness to Reason @romanschejbal from React to ReasonReact
  • 2. SYNTAX & TOOLCHAIN FOR OCAML
  • 6. OCaml? Why should I care? 🤔
  • 10. Compiler Errors We've found a bug for you! /workspace/reason/src/Student.re 20:30 19 │ let age = 2; 20 │ let greeting = greet(Student(1)); 21 │ 22 │ Js.log(greeting); This has type: int But somewhere wanted: string You can convert a int to a string with string_of_int.
  • 11. Good Support For Immutable Programming ✔
  • 12. Automatic Type Inference (less type definitions)
  • 15. let name = "Reason"; let age = 2; let greet = () => "Hello World!"; Language Basics
  • 16. Algebraic data types type name = option(string); /* None | Some(“Reason”) */ type state = { items: array(string), length: number }; type schoolPerson = | Teacher | Director | Student(string);
  • 17. type schoolPerson = Teacher | Director | Student(string); let greet = stranger => switch (stranger) { | Teacher => "Hey professor!" | Director => "Hello director." | Student("Richard") => "Still here Ricky?" | Student(anyOtherName) => "Hey, " ++ anyOtherName ++ "." }; greet(Teacher); /* Hey professor! */ greet(Student("OCaml")); /* Hey, OCaml. */ Variants & Pattern Matching
  • 18. Pattern Matching Exhaustiveness Warning number 8 /workspace/reason/src/Student.re 7:3-12:3 5 │ 6 │ let greet = stranger => 7 │ switch (stranger) { 8 │ | Teacher => "Hey professor!" . │ ... 11 │ /* | Student(anyOtherName) => "Hey " ++ anyOtherName ++ "." */ 12 │ }; 13 │ 14 │ print_endline(greet(Teacher)); You forgot to handle a possible value here, for example: Student ""
  • 19.
  • 20. type userT = option(string); let userOne = Some("X"); let userTwo = None; let printPlayerName = player => switch (player) { | Some(name) => "Logged in as " ++ name | None => "Not logged in" }; Option type
  • 21. Why Reason then? 🤔 “We want people to be able to use powerful, well- typed languages at work, not just in their free time.” - Jordan Walke
  • 23.
  • 24.
  • 25. How Do I Start 🧐
  • 27. flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | |- store.js | |- App.js | |- index.js | package.json | bsconfig.json | yarn.lock
  • 28. flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | |- store.js | |- App.js | |- index.js | package.json | bsconfig.json | yarn.lock yarn add bs-platform reason-react
  • 29. bsconfig.json { "name": "my-project", "namespace": true, "sources": [ { "dir": "src", "subdirs": true } ], "package-specs": { "module": "es6-global", "in-source": true }, "suffix": ".bs.js", "bs-dependencies": ["reason-react"], "reason": { "react-jsx": 2 } } flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | |- store.js | |- App.js | |- index.js | package.json | bsconfig.json | yarn.lock
  • 30. package.json { "name": "from-react-to-reason", "version": "0.1.0", "private": true, "dependencies": { "bs-platform": "^3.0.0", "flow-bin": "^0.72.0", "react": "^16.3.2", "react-dom": "^16.3.2", "react-redux": "^5.0.7", "react-scripts": "1.1.4", "reason-react": "^0.4.1", "redux": "^4.0.0", "redux-thunk": "^2.2.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject", "bsb": "bsb -make-world -w" } } flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | |- store.js | |- App.js | |- index.js | package.json | bsconfig.json | yarn.lock "bsb": "bsb -make-world -w"
  • 31. src/Types.re [@bs.deriving jsConverter] type flightInfo = { airline: string, price: int, duration: float, }; flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock Record Type
  • 32. src/Types.bs.js // Generated by BUCKLESCRIPT VERSION 3.0.0, PLEASE EDIT WITH CARE function flightInfoToJs(param) { return { airline: param[ /* airline */0], price: param[ /* price */1], duration: param[ /* duration */2] }; } function flightInfoFromJs(param) { return /* record */[ /* airline */param.airline, /* price */param.price, /* duration */param.duration ]; } export { flightInfoToJs , flightInfoFromJs , } /* No side effect */ flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 33. src/state/reducer/index.js import { type actionT, REQUEST_FLIGHTS, RECEIVE_FLIGHTS, FAILURE_FLIGHTS, } from ' ../action'; const initialState = { loading: false, flights: [], error: null, }; export default (state = initialState, action) => { switch (action.type) { case REQUEST_FLIGHTS: return { ...state, loading: true }; case RECEIVE_FLIGHTS: return { ...state, loading: false, flights: action.payload.flights }; case FAILURE_FLIGHTS: return { ...state, error: action.payload.error }; default: return state; } }; flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 34. src/state/reducer/Reducer.re type flightsData = array(Types.flightInfo); [@bs.deriving jsConverter] type state = { loading: bool, flights: flightsData, error: option(Js.Exn.t) }; let initialState = { loading: false, flights: [ ||], error: None }; let default = (state, action) => switch (action ##_type) { | "REQUEST_FLIGHTS" => { ...state, loading: true} | "RECEIVE_FLIGHTS" => { ...state, flights: Array.map(Types.flightInfoFromJs, action ##payload ##flights), loading: false, } | "FAILURE_FLIGHTS" => { ...state, error: action ##payload ##error} }; flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 35. src/state/reducer/Reducer.re Warning number 8 /Users/roman/workspace/from-react-to-reason/src/state/reducer/Reducer.re 21:3-30:3 19 │ 20 │ let default = (state, action) => 21 │ switch (action##_type) { 22 │ | "REQUEST_FLIGHTS" => {...state, loading: true} . │ ... 29 │ | “FAILURE_FLIGHTS" => {...state, error: action##payload##error} 30 │ }; 31 │ 32 │ /* let default = (state, action) => { You forgot to handle a possible value here, for example: ""
  • 36. src/state/reducer/Reducer.re type flightsData = array(Types.flightInfo); type state = { loading: bool, flights: flightsData, error: option(Js.Exn.t) }; let initialState = { loading: false, flights: [ ||], error: None }; let default = (state, action) => switch (action ##_type) { | "REQUEST_FLIGHTS" => { ...state, loading: true} | "RECEIVE_FLIGHTS" => { ...state, flights: Array.map(Types.flightInfoFromJs, action ##payload ##flights), loading: false, } | "FAILURE_FLIGHTS" => { ...state, error: action ##payload ##error} | _ => state }; flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock | _ => state
  • 37. src/state/action/index.js export const REQUEST_FLIGHTS = 'REQUEST_FLIGHTS'; export const RECEIVE_FLIGHTS = 'RECEIVE_FLIGHTS'; export const FAILURE_FLIGHTS = 'FAILURE_FLIGHTS'; export const fetchFlights = (where, when) => ( dispatch ) => { dispatch(requestFlights()); setTimeout( () => dispatch( receiveFlights([ { airline: 'British Airways', price: 390, duration: 3.5 }, { airline: 'KLM', price: 340, duration: 2.2 } ]) ), 3000 ); }; const requestFlights = () => ({ type: REQUEST_FLIGHTS }); const receiveFlights = (flights: Array<Object>) => ({ type: RECEIVE_FLIGHTS, payload: { flights } }); const failureFlights = (error: Error) => ({ type: FAILURE_FLIGHTS, payload: { error } }); flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 38. src/state/action/Actions.re let fetchFlights = (from, where, when) => (. dispatch) => { dispatch(requestFlights(from, where, when)); Js.Global.setTimeout( () => dispatch( receiveFlights( [| {airline: "British Airways", price: 390, duration: 3.5}, {airline: "KLM", price: 340, duration: 2.2}, |]), ), 3000, ); }; let requestFlights = (from, where, when) => { "type": "REQUEST_FLIGHTS", "payload": { "from": from, "where": where, "when": when, }, }; let receiveFlights = flights => { "type": "RECEIVE_FLIGHTS", "payload": { "flights": flights, }, }; ... flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | | |- Actions.re | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 39. src/state/store.js import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import rootReducer from './reducer/Reducer.bs'; const initialState = undefined; export default () => createStore( rootReducer, applyMiddleware(thunk), initialState ); flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | | |- Actions.re | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 40. What have we gained? 1. Can’t mess up our state shape Can we get more?
  • 41. src/state/action/Actions.re let fetchFlights = (from, where, when) => (. dispatch) => { dispatch(requestFlights(from, where, when)); Js.Global.setTimeout( () => dispatch( receiveFlights( [| {airline: "British Airways", price: 390, duration: 3.5}, {airline: "KLM", price: 340, duration: 2.2}, |]), ), 3000, ); }; let requestFlights = (from, where, when) => { "type": "REQUEST_FLIGHTS", "payload": { "from": from, "where": where, "when": when, }, }; let receiveFlights = flights => { "type": "RECEIVE_FLIGHTS", "payload": { "flights": flights, }, }; let failureFlights = error => { "type": "FAILURE_FLIGHTS", "payload": { "error": error, }, }; flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | | |- Actions.re | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 42. src/state/action/Actions.re type from = string; type where = string; type when = string; [@bs.deriving accessors] type action = | RequestFlights(from, where, when) | ReceiveFlights(array(Types.flightInfo)) | FailureFlights(Js.Exn.t); let fetchFlights = (from, where, when) => (. dispatch) => { dispatch(RequestFlights(from, where, when)); Js.Global.setTimeout( () => dispatch( ReceiveFlights( [| {airline: "British Airways", price: 390, duration: 3.5}, {airline: "KLM", price: 340, duration: 2.2}, |]), ), 3000, ); }; flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | | |- Actions.re | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 43. src/state/action/Reducer.re type flightsData = array(Types.flightInfo); [@bs.deriving jsConverter] type state = { loading: bool, flights: flightsData, error: option(Js.Exn.t), }; let initialState = { loading: false, flights: [ ||], error: None, }; let default = (state, action) => { Actions.( switch (action) { | RequestFlights(_, _, _) => { ...state, loading: true} | ReceiveFlights(flights) => { ...state, flights} | FailureFlights(error) => { ...state, error: Some(error)} } ) }; flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | | |- Actions.re | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 44. src/state/store.js import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import rootReducer from './reducer/Reducer.bs'; const fromReasonToJs = store => next => action => { if (action.tag !== undefined) { // reason action const { tag } = action; action = { type: `REASON_ACTION_${tag}`, tag, reasonAction: action }; } next(action); }; const reasonReducer = reducer => (state, action) => { if (action.reasonAction) return reducer(state, action.reasonAction); return reducer(state, { ...action, tag: -1 }); }; const initialState = undefined; export default () => createStore( reasonReducer(rootReducer), applyMiddleware(thunk, fromReasonToJs), initialState ); flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | |- state/ | | |- action/ | | | |- index.js | | | |- Actions.re | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 45. src/components/FlightList.re open ReasonReact; let component = statelessComponent("FlightsList"); let make = (~data, _children) => { ...component, render: _self => <div> ( Array.map( flightInfo => <div> <h2> (string(flightInfo.airline)) </h2> <p> (string("Price: $" ++ string_of_int(flightInfo.price))) ( string( " | Duration: " ++ string_of_float(flightInfo.duration) ++ "hours", ) ) </p> </div>, data, ) |> array ) </div>, }; let default = wrapReasonForJs(~component, jsProps => make(~data=jsProps ##data, jsProps ##children) ); flight-search/ |- public/ |- node_modules/ |- public/ |- src/ | |- components/ | | |- flight-list.js | | | |- FlightList.js | |- state/ | | |- action/ | | | |- index.js | | | |- Actions.re | | |- reducer/ | | | |- index.js | | | |- Reducer.re | | |- store.js | |- App.js | |- index.js | |- Types.re | package.json | bsconfig.json | yarn.lock
  • 46. What have we gained? 1. Can’t mess up our state shape 2. Can’t forget to handle actions 3. Can’t forget to handle edge cases
  • 47. Not covered… • Rendering a JS component from a Reason component • Using Variants for async stuff i.e. [@bs.module] external myJSComponent : ReasonReact.reactClass = “./my-js-component“; let make = (~name: string, ~age: option(int)=?, children) => ReasonReact.wrapJsForReason( ~reactClass=myJSComponent, ~props={"name": name, "age": Js.Nullable.fromOption(age)}, children, ); type async('a) = | Idle | Loading | Success('a) | Error(Js.Exn.t); type action = | Tick(unit) | Flights(Types.(async(array(flightInfo)))); http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipattern.html
  • 48.