SlideShare uma empresa Scribd logo
1 de 85
Baixar para ler offline
Functional Concepts in C#
Or “Who the F# Wrote This?”
https://github.com/mrdrbob/sd-code-camp-2016
Thanks Sponsors!
Let’s Manage Expectations!
What this talk is
A gentle introduction to functional
paradigms using a language you may
already be familiar with.
A comparison between OOP and
functional styles
A discussion on language expectations
What this talk
isn’t
“OOP is dead!”
“Functional all the things!”
“All code should look exactly like
this!” (Spoiler: it probably shouldn’t)
Who I am
Bob Davidson
C# / Web Developer 11 years
Blend Interactive
A guy who is generally interested in
and learning about functional
programming concepts
https://github.com/mrdrbob
Who I am Not
A functional programming expert who
says things like:
“All told, a monad in X is just a
monoid in the category of
endofunctors of X, with product ×
replaced by composition of
endofunctors and unit set by the
identity endofunctor.”
-Saunders Mac Lane
Let’s Build a Parser!
A highly simplified JSON-like syntax for strings and integers.
Integers
One or more digits
Strings
Starts & ends with double quote.
Quotes can be escaped with slash.
Slash can be escaped with slash.
Can be empty.
Iteration 1.0
The IParser<TValue> Interface
public interface IParser<TValue>  {
bool  TryParse(string raw,  out TValue value);
}
IntegerParser
public class IntegerParser :  IParser<int>  {
public bool  TryParse(string raw,  out int value)  {
value  = 0;
int x  = 0;
List<char> buffer  = new List<char>();
while (x  < raw.Length && char.IsDigit(raw[x]))  {
buffer.Add(raw[x]);
x  += 1;
}
if (x  == 0)
return false;
//  Deal  with  it.
value  = int.Parse(new string(buffer.ToArray()));
return true;
}
}
IntegerParser
public class IntegerParser :  IParser<int>  {
public bool  TryParse(string raw,  out int value)  {
value  = 0;
int x  = 0;
List<char> buffer  = new List<char>();
while (x  < raw.Length && char.IsDigit(raw[x]))  {
buffer.Add(raw[x]);
x  += 1;
}
if (x  == 0)
return false;
value  = int.Parse(new string(buffer.ToArray()));
return true;
}
}
StringParser
public class StringParser :  IParser<string>  {
public bool  TryParse(string raw,  out string value)  {
value  = null;
int x  = 0;
if (x  == raw.Length || raw[x]  != '"')
return false;
x  += 1;
List<char> buffer  = new List<char>();
while (x  < raw.Length && raw[x]  != '"')  {
if (raw[x]  == '')  {
x  += 1;
if (x  == raw.Length)
return false;
if (raw[x]  == '')
buffer.Add(raw[x]);
else if (raw[x]  == '"')
buffer.Add(raw[x]);
else
return false;
}  else {
buffer.Add(raw[x]);
}
x  += 1;
}
if (x  == raw.Length)
return false;
x  += 1;
value  = new string(buffer.ToArray());
return true;
}
}
Possible Issues
public class StringParser :  IParser<string>  {
public bool  TryParse(string raw,  out string value)  {
value  = null;
int x  = 0;
if (x  == raw.Length || raw[x]  != '"')
return false;
x  += 1;
List<char> buffer  = new List<char>();
while (x  < raw.Length && raw[x]  != '"')  {
if (raw[x]  == '')  {
x  += 1;
if (x  == raw.Length)
return false;
if (raw[x]  == '')
buffer.Add(raw[x]);
else if (raw[x]  == '"')
buffer.Add(raw[x]);
else
return false;
}  else {
buffer.Add(raw[x]);
}
x  += 1;
}
if (x  == raw.Length)
return false;
x  += 1;
value  = new string(buffer.ToArray());
return true;
}
}
Repeated checks against
running out of input
Easily missed logic for
moving input forward
No way to see how much
input was consumed / how
much is left
Hard to understand at a
glance what is happening
public class IntegerParser :  IParser<int>  {
public bool  TryParse(string raw,  out int value)  {
value  = 0;
int x  = 0;
List<char> buffer  = new List<char>();
while (x  < raw.Length && char.IsDigit(raw[x]))  {
buffer.Add(raw[x]);
x  += 1;
}
if (x  == 0)
return false;
//  Deal  with  it.
value  = int.Parse(new string(buffer.ToArray()));
return true;
}
}
Rethinking the Parser
Make a little more generic / reusable
Break the process down into a series of rules which can be composed to make new
parsers from existing parsers
Build a framework that doesn’t rely on strings, but rather a stream of tokens
Iteration 2.0
Composition
[Picture of Legos Here]
One or More
Times
A Parser Built on Rules (Integer Parser)
[0-9]
Ignore Latter
Keep Latter
Zero or More Times
Any of these
NotKeep Latter
A Parser Built on Rules (String Parser)
“
 “
Keep Latter
 
Any of these
 “
“
A Set of Rules
Match Quote
Match Slash
Match Digit
Match Then Keep
Match Then Ignore
Match Any
Match Zero or More Times
Match One or More Times
Not
Rethinking the Source
Handle tokens other than chars (such as byte streams, pre-lexed tokens, etc)
Need the ability to continue parsing after a success
Need the ability to reset after a failure
Rethinking the Source
public interface ISource<Token>  {
Token Current {  get;  }
bool  HasMore {  get;  }
int CurrentIndex {  get;  }
void Move(int index);
}
public class StringSource :  ISource<char>  {
readonly  string  value;
int index;
public StringSource(string value)  {  this.value  = value;  }
public char Current  => value[index];
public int CurrentIndex  => index;
public bool  HasMore  => index  < value.Length;
public void Move(int index)  =>  this.index  =  index;
}
Creating a Rule
public interface IRule<Token,  TResult>  {
bool  TryParse(ISource<Token> source,  out TResult result);
}
Char Matches...
public class CharIsQuote :  IRule<char,  char>  {
public bool  TryParse(ISource<char> source,  out
char result)  {
result  = default(char);
if (!source.HasMore)
return false;
if (source.Current != '"')
return false;
result  = source.Current;
source.Move(source.CurrentIndex + 1);
return true;
}
}
public class CharIs :  IRule<char,  char>  {
readonly  char toMatch;
public CharIs(char toMatch)  {  this.toMatch  =
toMatch;  }
public bool  TryParse(ISource<char> source,  out char
result)  {
result  = default(char);
if (!source.HasMore)
return false;
if (source.Current != toMatch)
return false;
result  = source.Current;
source.Move(source.CurrentIndex + 1);
return true;
}
}
Char Matches...
public abstract class CharMatches :  IRule<char,  char>  {
protected abstract bool  IsCharMatch(char c);
public bool  TryParse(ISource<char> source,  out char result)  {
result  = default(char);
if (!source.HasMore)
return false;
if (!IsCharMatch(source.Current))
return false;
result  = source.Current;
source.Move(source.CurrentIndex + 1);
return true;
}
}
public class CharIsDigit :  CharMatches  {
protected override  bool  IsCharMatch(char c)  =>  char.IsDigit(c);
}
public class CharIs :  CharMatches  {
readonly  char toMatch;
public CharIs(char toMatch)  {  this.toMatch  = toMatch;  }
protected override  bool  IsCharMatch(char c)  =>  c  ==  toMatch;
}
First Match (or Any)
public class FirstMatch<Token,  TResult>  :  IRule<Token,  TResult>  {
readonly  IRule<Token,  TResult>[]  rules;
public FirstMatch(IRule<Token,  TResult>[]  rules)  {  this.rules  = rules;  }
public bool  TryParse(ISource<Token> source,  out TResult result)  {
foreach(var  rule  in  rules)  {
int originalIndex  = source.CurrentIndex;
if (rule.TryParse(source,  out  result))
return true;
source.Move(originalIndex);
}
result  = default(TResult);
return false;
}
}
Match Then... public abstract class MatchThen<Token,  TLeft,  TRight,  TOut>  :  IRule<Token,  TOut>  {
readonly  IRule<Token,  TLeft> leftRule;
readonly  IRule<Token,  TRight> rightRule;
protected abstract TOut Combine(TLeft leftResult,  TRight rightResult);
public MatchThen(IRule<Token,  TLeft> leftRule,  IRule<Token,  TRight> rightRule)  {
this.leftRule  = leftRule;
this.rightRule  = rightRule;
}
public bool  TryParse(ISource<Token> source,  out TOut result)  {
int originalIndex  = source.CurrentIndex;
result  = default(TOut);
TLeft leftResult;
if (!leftRule.TryParse(source,  out  leftResult))  {
source.Move(originalIndex);
return false;
}
TRight rightResult;
if (!rightRule.TryParse(source,  out  rightResult))  {
source.Move(originalIndex);
return false;
}
result  = Combine(leftResult,  rightResult);
return true;
}
}
Match Then...
public class MatchThenKeep<Token,  TLeft,  TRight>  :  MatchThen<Token,  TLeft,  TRight,  TRight>  {
public MatchThenKeep(IRule<Token,  TLeft> leftRule,  IRule<Token,  TRight> rightRule)  :  base(leftRule,  rightRule)  {  }
protected override  TRight Combine(TLeft leftResult,  TRight rightResult)  =>  rightResult;
}
public class MatchThenIgnore<Token,  TLeft,  TRight>  :  MatchThen<Token,  TLeft,  TRight,  TLeft>  {
public MatchThenIgnore(IRule<Token,  TLeft> leftRule,  IRule<Token,  TRight> rightRule)  :  base(leftRule,  rightRule)  {  }
protected override  TLeft Combine(TLeft leftResult,  TRight rightResult)  =>  leftResult;
}
Invert Rule (Not)
public class Not<Token,  TResult>  :  IRule<Token,  Token>  {
readonly  IRule<Token,  TResult> rule;
public Not(IRule<Token,  TResult> rule)  {  this.rule  = rule;  }
public bool  TryParse(ISource<Token> source,  out Token result)  {
result  = default(Token);
if (!source.HasMore)
return false;
int originalIndex  = source.CurrentIndex;
TResult throwAwayResult;
bool  matches  = rule.TryParse(source,  out  throwAwayResult);
if (matches)
{
source.Move(originalIndex);
return false;
}
source.Move(originalIndex);
result  = source.Current;
source.Move(originalIndex  + 1);
return true;
}
}
Spot the bug!
Many (Once, Zero, and more times)
public class Many<Token,  TResult>  :  IRule<Token,  TResult[]>  {
readonly  IRule<Token,  TResult> rule;
readonly  bool  requireAtLeastOne;
public Many(IRule<Token,  TResult> rule,  bool requireAtLeastOne)  {  this.rule  = rule;  this.requireAtLeastOne  = requireAtLeastOne;  }
public bool  TryParse(ISource<Token> source,  out TResult[]  results)  {
List<TResult> buffer  = new List<TResult>();
while (source.HasMore)  {
int originalIndex  = source.CurrentIndex;
TResult result;
bool  matched  = rule.TryParse(source,  out  result);
if (!matched)  {
source.Move(originalIndex);
break;
}
buffer.Add(result);
}
if (requireAtLeastOne  && buffer.Count == 0)  {
results  = null;
return false;
}
results  = buffer.ToArray();
return true;
}
}
Map Result
public abstract class MapTo<Token,  TIn,  TOut>  :  IRule<Token,  TOut>  {
readonly  IRule<Token,  TIn> rule;
protected MapTo(IRule<Token,  TIn> rule)  {  this.rule  = rule;  }
protected abstract TOut Convert(TIn value);
public bool  TryParse(ISource<Token> source,  out TOut result)  {
result  = default(TOut);
int originalIndex  = source.CurrentIndex;
TIn resultIn;
if (!rule.TryParse(source,  out  resultIn))  {
source.Move(originalIndex);
return false;
}
result  = Convert(resultIn);
return true;
}
}
Map Result
public class JoinText :  MapTo<char,  char[],  string>  {
public JoinText(IRule<char,  char[]> rule)  :  base(rule)  {  }
protected override  string  Convert(char[]  value)  =>  new  string(value);
}
public class MapToInteger :  MapTo<char,  string,  int>  {
public MapToInteger(IRule<char,  string> rule)  :  base(rule)  {  }
protected override  int Convert(string value)  =>  int.Parse(value);
}
Putting the blocks together
var  quote  = new CharIs('"');
var  slash  = new CharIs('');
var  escapedQuote  = new MatchThenKeep<char,  char,  char>(slash,  quote);
var  escapedSlash  = new MatchThenKeep<char,  char,  char>(slash,  slash);
var  notQuote  = new Not<char,  char>(quote);
var  insideQuoteChar  = new FirstMatch<char,  char>(new[]  {
(IRule<char,  char>)escapedQuote,
escapedSlash,
notQuote
});
var  insideQuote  = new Many<char,  char>(insideQuoteChar,  false);
var  insideQuoteAsString  = new JoinText(insideQuote);
var  openQuote  = new MatchThenKeep<char,  char,  string>(quote,  
insideQuoteAsString);
var  fullQuote  = new MatchThenIgnore<char,  string,  char>(openQuote,  quote);
var  source  = new StringSource(raw);
string  asQuote;
if (fullQuote.TryParse(source,  out  asQuote))
return asQuote;
source.Move(0);
int asInteger;
if (digitsAsInt.TryParse(source,  out  asInteger))
return asInteger;
return null;
var  digit  = new CharIsDigit();
var  digits  = new Many<char,  char>(digit,  true);
var  digitsString  = new JoinText(digits);
var  digitsAsInt  = new MapToInteger(digitsString);
A Comparison
A Comparison
A Comparison
What an Improvement!
A Comparison (just the definition)
Room for Improvement
public abstract class MatchThen<Token,  TLeft,  TRight,  TOut>  :  IRule<Token,  TOut>  {
readonly  IRule<Token,  TLeft> leftRule;
readonly  IRule<Token,  TRight> rightRule;
protected abstract TOut Combine(TLeft leftResult,  TRight rightResult);
public MatchThen(IRule<Token,  TLeft> leftRule,  IRule<Token,  TRight> rightRule)  {
this.leftRule  = leftRule;
this.rightRule  = rightRule;
}
public bool  TryParse(ISource<Token> source,  out TOut result)  {
int originalIndex  = source.CurrentIndex;
result  = default(TOut);
TLeft leftResult;
if (!leftRule.TryParse(source,  out  leftResult))  {
source.Move(originalIndex);
return false;
}
TRight rightResult;
if (!rightRule.TryParse(source,  out  rightResult))  {
source.Move(originalIndex);
return false;
}
result  = Combine(leftResult,  rightResult);
return true;
}
}
Out parameter :(
Managing the source’s index
Iteration 2.1
Immutability
An Immutable Source
public interface ISource<Token>  {
Token Current {  get;  }
bool  HasMore {  get;  }
int CurrentIndex {  get;  }
void Move(int index);
}
public interface ISource<Token>  {
Token Current {  get;  }
bool  HasMore {  get;  }
ISource<Token> Next();
}
An Immutable Source
public class StringSource :  ISource<char>  {
readonly  string  value;
int index;
public StringSource(string value)  {  
this.value  = value;  }
public char Current  => value[index];
public int CurrentIndex  => index;
public bool  HasMore  => index  < value.Length;
public void Move(int index)  =>  this.index  =  index;
}
public class StringSource :  ISource<char>  {
readonly  string  value;
readonly  int index;
public StringSource(string value,  int index =  0)  {  
this.value  = value;  this.index  = index;  }
public char Current  => value[index];
public bool  HasMore  => index  < value.Length;
public ISource<char> Next()  =>  
new  StringSource(value,  index +  1);
}
Ditch the Out
public class Result<Token,  TValue>  {
public bool  Success {  get;  }
public TValue Value {  get;  }
public string  Message {  get;  }
public ISource<Token> Next {  get;  }
public Result(bool success,  TValue value,  string message,  ISource<Token> next)  {
Success = success;
Value = value;
Message = message;
Next = next;
}
}
public interface IRule<Token,  TValue>  {
Result<Token,  TValue> TryParse(ISource<Token> source);
}
Char Matches...
public abstract class CharMatches :  IRule<char,  char>  {
protected abstract bool  IsCharMatch(char c);
public bool  TryParse(ISource<char> source,  out char result)  {
result  = default(char);
if (!source.HasMore)
return false;
if (!IsCharMatch(source.Current))
return false;
result  = source.Current;
source.Move(source.CurrentIndex + 1);
return true;
}
}
public abstract class CharMatches :  IRule<char,  char>  {
protected abstract bool  IsCharMatch(char c);
public Result<char,  char> TryParse(ISource<char> source)  {
if (!source.HasMore)
return new Result<char,  char>(false,  '0',  "Unexpected  EOF",  null);
if (!IsCharMatch(source.Current))
return new Result<char,  char>(false,  '0',  $"Unexpected  char:  {source.Current}",  null);
return new Result<char,  char>(true,  source.Current,  null,  source.Next());
}
}
These Don’t Change
public class CharIsDigit :  CharMatches  {
protected override  bool  IsCharMatch(char c)  =>  char.IsDigit(c);
}
public class CharIs :  CharMatches  {
readonly  char toMatch;
public CharIs(char toMatch)  {  this.toMatch  = toMatch;  }
protected override  bool  IsCharMatch(char c)  =>  c  ==  toMatch;
}
First Match
public class FirstMatch<Token,  TResult>  :  IRule<Token,  TResult>  {
readonly  IRule<Token,  TResult>[]  rules;
public FirstMatch(IRule<Token,  TResult>[]  rules)  {  this.rules  = rules;  }
public bool  TryParse(ISource<Token> source,  out TResult result)  {
foreach(var  rule  in  rules)  {
int originalIndex  = source.CurrentIndex;
if (rule.TryParse(source,  out  result))
return true;
source.Move(originalIndex);
}
result  = default(TResult);
return false;
}
}
public class FirstMatch<Token,  TResult>  :  IRule<Token,  TResult>  {
readonly  IRule<Token,  TResult>[]  rules;
public FirstMatch(IRule<Token,  TResult>[]  rules)  {  this.rules  = rules;  }
public Result<Token,  TResult> TryParse(ISource<Token> source)  {
foreach  (var  rule  in  rules)  {
var  result  = rule.TryParse(source);
if (result.Success)
return result;
}
return new Result<Token,  TResult>(false,  default(TResult),  "No  rule  matched",  null);
}
}
Match Then...
public bool  TryParse(ISource<Token> source,  out TOut result)  {
int originalIndex  = source.CurrentIndex;
result  = default(TOut);
TLeft leftResult;
if (!leftRule.TryParse(source,  out  leftResult))  {
source.Move(originalIndex);
return false;
}
TRight rightResult;
if (!rightRule.TryParse(source,  out  rightResult))  {
source.Move(originalIndex);
return false;
}
result  = Combine(leftResult,  rightResult);
return true;
}
public Result<Token,  TOut> TryParse(ISource<Token> source)  {
var  leftResult  = leftRule.TryParse(source);
if (!leftResult.Success)
return new Result<Token,  TOut>(false,  default(TOut),  leftResult.Message,  null);
var  rightResult  = rightRule.TryParse(leftResult.Next);
if (!rightResult.Success)
return new Result<Token,  TOut>(false,  default(TOut),  rightResult.Message,  null);
var  result  = Combine(leftResult.Value,  rightResult.Value);
return new Result<Token,  TOut>(true,  result,  null,  rightResult.Next);
}
Invert Rule (Not)
public class Not<Token,  TResult>  :  IRule<Token,  Token>  {
readonly  IRule<Token,  TResult> rule;
public Not(IRule<Token,  TResult> rule)  {  this.rule  = rule;  }
public bool  TryParse(ISource<Token> source,  out Token result)  {
result  = default(Token);
if (!source.HasMore)
return false;
int originalIndex  = source.CurrentIndex;
TResult throwAwayResult;
bool  matches  = rule.TryParse(source,  out  throwAwayResult);
if (matches)
{
source.Move(originalIndex);
return false;
}
source.Move(originalIndex);
result  = source.Current;
source.Move(originalIndex  + 1);
return true;
}
}
public class Not<Token,  TResult>  :  IRule<Token,  Token>  {
readonly  IRule<Token,  TResult> rule;
public Not(IRule<Token,  TResult> rule)  {  this.rule  = rule;  }
public Result<Token,  Token> TryParse(ISource<Token> source)  {
if (!source.HasMore)
return new Result<Token,  Token>(false,  default(Token),  "Unexpected  
EOF",  null);
var  result  = rule.TryParse(source);
if (result.Success)
return new Result<Token,  Token>(false,  default(Token),  "Unexpected  
match",  null);
return new Result<Token,  Token>(true,  source.Current,  null,  
source.Next());
}
}
Getting Better, but...
Still Room for Improvement
public class Result<Token,  TValue>  {
public bool  Success {  get;  }
public TValue Value {  get;  }
public string  Message {  get;  }
public ISource<Token> Next {  get;  }
public Result(bool success,  TValue value,  string message,  ISource<Token> next)  {
Success = success;
Value = value;
Message = message;
Next = next;
}
}
Only valid when Success = true
Only valid when Success = false
Iteration 2.2
Discriminated Unions and Pattern Matching (sorta)
Two States (Simple “Result” Example)
public interface IResult {  }
public class SuccessResult<TValue>  :  IResult  {
public TValue Value {  get;  }
public SuccessResult(TValue value)  {  Value = value;  }
}
public class ErrorResult :  IResult  {
public string  Message {  get;  }
public ErrorResult(string message)  {  Message = message;  }
}
Two States (The Matching)
IResult result  = ParseIt();
if (result  is  SuccessResult<string>)  {
var  success  = (SuccessResult<string>)result;
Console.WriteLine($"SUCCESS:  {success.Value}");
}  else if (result  is  ErrorResult)  {
var  error  = (ErrorResult)result;
Console.WriteLine($"ERR:  {error.Message}");
}
Pattern Matching(ish)
public interface IResult<TValue>  {
T  Match<T>(Func<SuccessResult<TValue>,  T> success,
Func<ErrorResult<TValue>,  T> error);
}
public class SuccessResult<TValue>  :  IResult<TValue>  {
public TValue Value {  get;  }
public SuccessResult(TValue value)  {  Value = value;  }
public T  Match<T>(Func<SuccessResult<TValue>,  T> success,
Func<ErrorResult<TValue>,  T> error)  =>  success(this);
}
public class ErrorResult<TValue>  :  IResult<TValue>
{
public string  Message {  get;  }
public ErrorResult(string message)  {  Message = message;  }
public T  Match<T>(Func<SuccessResult<TValue>,  T> success,
Func<ErrorResult<TValue>,  T> error)  =>  error(this);
}
Pattern Matching(ish)
IResult<string> result  = ParseIt();
string  message  = result.Match(
success  => $"SUCCESS:  ${success.Value}",
error  => $"ERR:  {error.Message}");
Console.WriteLine(message);
IResult result  = ParseIt();
if (result  is  SuccessResult<string>)  {
var  success  = (SuccessResult<string>)result;
Console.WriteLine($"SUCCESS:  {success.Value}");
}  else if (result  is  ErrorResult)  {
var  error  = (ErrorResult)result;
Console.WriteLine($"ERR:  {error.Message}");
}
The Match
Method
Forces us to handle all cases
Gives us an object with only valid
properties for that state
The New IResult
public interface IResult<Token,  TValue>  {
T  Match<T>(Func<FailResult<Token,  TValue>,  T> fail,
Func<SuccessResult<Token,  TValue>,  T> success);
}
public class FailResult<Token,  TValue>  :  IResult<Token,  TValue>  {
public string  Message {  get;  }
public FailResult(string message)  {  Message = message;  }
public T  Match<T>(Func<FailResult<Token,  TValue>,  T> fail,
Func<SuccessResult<Token,  TValue>,  T> success)  =>  fail(this);
}
public class SuccessResult<Token,  TValue>  :  IResult<Token,  TValue>  {
public TValue Value {  get;  }
public ISource<Token> Next {  get;  }
public SuccessResult(TValue value,  ISource<Token> next)  {  Value = value;  Next = next;  }
public T  Match<T>(Func<FailResult<Token,  TValue>,  T> fail,
Func<SuccessResult<Token,  TValue>,  T> success)  =>  success(this);
}
ISource also Represents Two States
public interface ISource<Token>  {
Token Current {  get;  }
bool  HasMore {  get;  }
ISource<Token> Next();
}
Only valid when HasMore = true
The New ISource
public interface ISource<Token>  {
T  Match<T>(Func<EmtySource<Token>,  T> empty,
Func<SourceWithMoreContent<Token>,  T> hasMore);
}
public class EmtySource<Token>  :  ISource<Token>  {
//  No  properties!    No  state!    Let's  just  make  it  singleton.
EmtySource()  {  }
public static readonly  EmtySource<Token> Instance  = new EmtySource<Token>();
public T  Match<T>(Func<EmtySource<Token>,  T> empty,
Func<SourceWithMoreContent<Token>,  T> hasMore)  =>  empty(this);
}
public class SourceWithMoreContent<Token>  :  ISource<Token>  {
readonly  Func<ISource<Token>> getNext;
public SourceWithMoreContent(Token current,  Func<ISource<Token>> getNext)  {  Current = current;  this.getNext  = getNext;  }
public Token Current {  get;  set;  }
public ISource<Token> Next()  =>  getNext();
public T  Match<T>(Func<EmtySource<Token>,  T> empty,
Func<SourceWithMoreContent<Token>,  T> hasMore)  =>  hasMore(this);
}
Make a String Source
public static class StringSource {
public static ISource<char> Create(string value,  int index =  0)  {
if (index  >= value.Length)
return EmtySource<char>.Instance;
return new SourceWithMoreContent<char>(value[index],  ()  => Create(value,  index  + 1));
}
}
public static ISource<char> Create(string  value,  int index  = 0)
=> index  >= value.Length
? (ISource<char>)EmtySource<char>.Instance
: new SourceWithMoreContent<char>(value[index],  ()  => Create(value,  index  + 1));
Char Matches... public abstract class CharMatches :  IRule<char,  char>  {
protected abstract bool  IsCharMatch(char c);
public Result<char,  char> TryParse(ISource<char> source)  {
if (!source.HasMore)
return new Result<char,  char>(false,  '0',  "Unexpected  EOF",  null);
if (!IsCharMatch(source.Current))
return new Result<char,  char>(false,  '0',  $"Unexpected  char:  {source.Current}",  null);
return new Result<char,  char>(true,  source.Current,  null,  source.Next());
}
}
public abstract class CharMatches :  IRule<char,  char>  {
protected abstract bool  IsCharMatch(char c);
public IResult<char,  char> TryParse(ISource<char> source)  {
var  result  = source.Match(
empty  => (IResult<char,  char>)new FailResult<char,  char>("Unexpected  EOF"),
hasMore  =>
{
if (!IsCharMatch(hasMore.Current))
return new FailResult<char,  char>($"Unexpected  char:  {hasMore.Current}");
return new SuccessResult<char,  char>(hasMore.Current,  hasMore.Next());
});
return result;
}
}
public IResult<char,  char> TryParse(ISource<char> source)
=> source.Match(
empty  => new FailResult<char,  char>("Unexpected  EOF"),
hasMore  => IsCharMatch(hasMore.Current)
? new SuccessResult<char,  char>(hasMore.Current,  hasMore.Next())
: (IResult<char,  char>)new FailResult<char,  char>($"Unexpected  char:  {hasMore.Current}")
);
Match Then...
public IResult<Token,  TOut> TryParse(ISource<Token> source)  {
var  leftResult  = leftRule.TryParse(source);
var  finalResult  = leftResult.Match(
leftFail  => new FailResult<Token,  TOut>(leftFail.Message),
leftSuccess  => {
var  rightResult  = rightRule.TryParse(leftSuccess.Next);
var  rightFinalResult  = rightResult.Match(
rightFail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(rightFail.Message),
rightSuccess  => {
var  finalValue  = Combine(leftSuccess.Value,  rightSuccess.Value);
return new SuccessResult<Token,  TOut>(finalValue,  rightSuccess.Next);
});
return rightFinalResult;
});
return finalResult;
}
public Result<Token,  TOut> TryParse(ISource<Token> source)  {
var  leftResult  = leftRule.TryParse(source);
if (!leftResult.Success)
return new Result<Token,  TOut>(false,  default(TOut),  leftResult.Message,  null);
var  rightResult  = rightRule.TryParse(leftResult.Next);
if (!rightResult.Success)
return new Result<Token,  TOut>(false,  default(TOut),  rightResult.Message,  null);
var  result  = Combine(leftResult.Value,  rightResult.Value);
return new Result<Token,  TOut>(true,  result,  null,  rightResult.Next);
}
public IResult<Token,  TOut> TryParse(ISource<Token> source)
=> leftRule.TryParse(source).Match(
leftFail  => new FailResult<Token,  TOut>(leftFail.Message),
leftSuccess  =>
rightRule.TryParse(leftSuccess.Next).Match(
rightFail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(rightFail.Message),
rightSuccess  => new SuccessResult<Token,  TOut>(Combine(leftSuccess.Value,  rightSuccess.Value),  
rightSuccess.Next)
)
);
Invert Rule (Not)
public Result<Token,  Token> TryParse(ISource<Token> source)  {
if (!source.HasMore)
return new Result<Token,  Token>(false,  default(Token),  "Unexpected  EOF",  null);
var  result  = rule.TryParse(source);
if (result.Success)
return new Result<Token,  Token>(false,  default(Token),  "Unexpected  match",  null);
return new Result<Token,  Token>(true,  source.Current,  null,  source.Next());
}
public IResult<Token,  Token> TryParse(ISource<Token> source)
=> source.Match(
empty  => new FailResult<Token,  Token>("Unexpected  EOF"),
current  => rule.TryParse(current).Match(
fail  => new SuccessResult<Token,  Token>(current.Current,  current.Next()),
success  => (IResult<Token,  Token>)new FailResult<Token,  Token>("Unexpected  match")
)
);
That’s nice but...
Let’s Be Honest
All these `new` objects are ugly.
var  quote  = new CharIs('"');
var  slash  = new CharIs('');
var  escapedQuote  = new MatchThenKeep<char,  char,  
char>(slash,  quote);
var  escapedSlash  = new MatchThenKeep<char,  char,  
char>(slash,  slash);
var  notQuote  = new Not<char,  char>(quote);
var  insideQuoteChar  = new FirstMatch<char,  char>(new[]  {
(IRule<char,  char>)escapedQuote,
escapedSlash,
notQuote
});
var  insideQuote  = new Many<char,  char>(insideQuoteChar,  
false);
var  insideQuoteAsString  = new JoinText(insideQuote);
var  openQuote  = new MatchThenKeep<char,  char,  
string>(quote,  insideQuoteAsString);
var  fullQuote  = new MatchThenIgnore<char,  string,  
char>(openQuote,  quote);
Also
Single method interfaces are lame*.
It’s effectively a delegate.
public interface IRule<Token,  TValue>  {
IResult<Token,  TValue> TryParse(ISource<Token> source);
}
*In  a  non-­scientific  poll  of  people  who  agree  with  me,  100%  of  
respondents  confirmed  this  statement.    Do  not  question  its  validity.
Iteration 3.0
Functions as First Class Citizens
A Rule is a Delegate is a Function
public interface IRule<Token,  TValue>  {
IResult<Token,  TValue> TryParse(ISource<Token> source);
}
public delegate  IResult<Token,  TValue> Rule<Token,  TValue>(ISource<Token> source);
Char Matches...
public abstract class CharMatches :  IRule<char,  char>  {
protected abstract bool  IsCharMatch(char c);
public IResult<char,  char> TryParse(ISource<char> source)
=>  source.Match(
empty =>  new FailResult<char,  char>("Unexpected EOF"),
hasMore  =>  IsCharMatch(hasMore.Current)
?  new  SuccessResult<char,  char>(hasMore.Current,  hasMore.Next())
:  (IResult<char,  char>)new  FailResult<char,  char>($"Unexpected  char:  {hasMore.Current}")
);
}
public static class Rules {
public static Rule<char,  char> CharMatches(Func<char,  bool> isMatch)
=>  (source)  =>  source.Match(
empty =>  new FailResult<char,  char>("Unexpected EOF"),
hasMore  =>  isMatch(hasMore.Current)
?  new  SuccessResult<char,  char>(hasMore.Current,  hasMore.Next())
:  (IResult<char,  char>)new  FailResult<char,  char>($"Unexpected  char:  {hasMore.Current}")
);
}
public static Rule<char,  char> CharIsDigit()  => CharMatches(char.IsDigit);
public static Rule<char,  char> CharIs(char c)  => CharMatches(x  => x  == c);
Then (Keep|Ignore)
public static Rule<Token,  TOut> MatchThen<Token,  TLeft,  TRight,  TOut>(this Rule<Token,  TLeft> leftRule,  Rule<Token,  
TRight> rightRule,  Func<TLeft,  TRight,  TOut> convert)
=> (source)  => leftRule(source).Match(
leftFail  => new FailResult<Token,  TOut>(leftFail.Message),
leftSuccess  =>
rightRule(leftSuccess.Next).Match(
rightFail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(rightFail.Message),
rightSuccess  => new SuccessResult<Token,  TOut>(convert(leftSuccess.Value,  rightSuccess.Value),  
rightSuccess.Next)
)
);
public static Rule<Token,  TRight> MatchThenKeep<Token,  TLeft,  TRight>(this Rule<Token,  TLeft> leftRule,  Rule<Token,  
TRight> rightRule)
=> MatchThen(leftRule,  rightRule,  (left,  right)  => right);
public static Rule<Token,  TLeft> MatchThenIgnore<Token,  TLeft,  TRight>(this Rule<Token,  TLeft> leftRule,  Rule<Token,  
TRight> rightRule)
=> MatchThen(leftRule,  rightRule,  (left,  right)  => left);
Not, MapTo, JoinText, MapToInteger
public static Rule<Token,  Token> Not<Token,  TResult>(this Rule<Token,  TResult> rule)
=> (source)  => source.Match(
empty  => new FailResult<Token,  Token>("Unexpected  EOF"),
current  => rule(current).Match(
fail  => new SuccessResult<Token,  Token>(current.Current,  current.Next()),
success  => (IResult<Token,  Token>)new FailResult<Token,  Token>("Unexpected  match")
)
);
public static Rule<Token,  TOut> MapTo<Token,  TIn,  TOut>(this Rule<Token,  TIn> rule,  Func<TIn,  TOut> convert)
=> (source)  => rule(source).Match(
fail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(fail.Message),
success  => new SuccessResult<Token,  TOut>(convert(success.Value),  success.Next)
);
public static Rule<char,  string> JoinText(this Rule<char,  char[]> rule)
=> MapTo(rule,  (x)  => new string(x));
public static Rule<char,  int> MapToInteger(this Rule<char,  string> rule)
=> MapTo(rule,  (x)  => int.Parse(x));
Example Usage
var  quote  = Rules.CharIs('"');
var  slash  = Rules.CharIs('');
var  escapedQuote  =  Rules.MatchThenKeep(slash,  quote);
var  escapedSlash  = slash.MatchThenKeep(slash);
The Original 2.0 Definition
var  quote  = new CharIs('"');
var  slash  = new CharIs('');
var  escapedQuote  = new MatchThenKeep<char,  char,  char>(slash,  quote);
var  escapedSlash  = new MatchThenKeep<char,  char,  char>(slash,  slash);
var  notQuote  = new Not<char,  char>(quote);
var  insideQuoteChar  = new FirstMatch<char,  char>(new[]  {
(IRule<char,  char>)escapedQuote,
escapedSlash,
notQuote
});
var  insideQuote  = new Many<char,  char>(insideQuoteChar,  false);
var  insideQuoteAsString  = new JoinText(insideQuote);
var  openQuote  = new MatchThenKeep<char,  char,  string>(quote,  
insideQuoteAsString);
var  fullQuote  = new MatchThenIgnore<char,  string,  char>(openQuote,  quote);
var  source  = new StringSource(raw);
string  asQuote;
if (fullQuote.TryParse(source,  out  asQuote))
return asQuote;
source.Move(0);
int asInteger;
if (digitsAsInt.TryParse(source,  out  asInteger))
return asInteger;
return null;
var  digit  = new CharIsDigit();
var  digits  = new Many<char,  char>(digit,  true);
var  digitsString  = new JoinText(digits);
var  digitsAsInt  = new MapToInteger(digitsString);
The Updated 3.0 Definition
var  quote  = Rules.CharIs('"');
var  slash  = Rules.CharIs('');
var  escapedQuote  = slash.MatchThenKeep(quote);
var  escapedSlash  = slash.MatchThenKeep(slash);
var  notQuote  = quote.Not();
var  fullQuote  = quote
.MatchThenKeep(
Rules.FirstMatch(
escapedQuote,
escapedSlash,
notQuote
).Many().JoinText()
)
.MatchThenIgnore(quote);
var  finalResult  = Rules.FirstMatch(
fullQuote.MapTo(x  => (object)x),
digit.MapTo(x  => (object)x)
);
var  source  = StringSource.Create(raw);
return finalResult(source).Match(
fail  => null,
success  => success.Value
);
var  integer  = Rules.CharIsDigit()
.Many(true)
.JoinText()
.MapToInteger();
A Comparison V1 -> V3
A Comparison V1 -> V3
A Comparison V1 -> V3 (Just Definition)
Looks Great!
My co-workers are going to kill me
Is it a good idea?
public static Rule<Token,  Token> Not<Token,  TResult>(this Rule<Token,  TResult> rule)
=> (source)  => source.Match(
empty  => new FailResult<Token,  Token>("Unexpected  EOF"),
current  => rule(current).Match(
fail  => new SuccessResult<Token,  Token>(current.Current,  current.Next()),
success  => (IResult<Token,  Token>)new FailResult<Token,  Token>("Unexpected  match")
)
);
public static Rule<Token,  TOut> MapTo<Token,  TIn,  TOut>(this Rule<Token,  TIn> rule,  Func<TIn,  TOut> convert)
=> (source)  => rule(source).Match(
fail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(fail.Message),
success  => new SuccessResult<Token,  TOut>(convert(success.Value),  success.Next)
);
public static Rule<char,  string> JoinText(this Rule<char,  char[]> rule)
=> MapTo(rule,  (x)  => new string(x));
public static Rule<char,  int> MapToInteger(this Rule<char,  string> rule)
=> MapTo(rule,  (x)  => int.Parse(x));
Limitations
“At Zombocom, the only limit…
is yourself.”
1. Makes a LOT of short-lived
objects (ISources, IResults).
2. As written currently, you will end
up with the entire thing in
memory.
3. Visual Studio’s Intellisense
struggles with nested lambdas.
4. Frequently requires casts to solve
type inference problems.
5. It’s not very C#.
Let’s Review
Iterations
Iteration 1.0: Procedural
Iteration 2.0: Making Compositional
with OOP
Iteration 2.1: Immutability
Iteration 2.2: Discriminated Unions and
Pattern Matching
Iteration 3.0: Functions as First Class
Citizens
That’s All
Thanks for coming and staying awake!

Mais conteúdo relacionado

Mais procurados

String in python use of split method
String in python use of split methodString in python use of split method
String in python use of split methodvikram mahendra
 
The Swift Compiler and Standard Library
The Swift Compiler and Standard LibraryThe Swift Compiler and Standard Library
The Swift Compiler and Standard LibrarySantosh Rajan
 
Learn python in 20 minutes
Learn python in 20 minutesLearn python in 20 minutes
Learn python in 20 minutesSidharth Nadhan
 
Logic programming a ruby perspective
Logic programming a ruby perspectiveLogic programming a ruby perspective
Logic programming a ruby perspectiveNorman Richards
 
Coding in Kotlin with Arrow NIDC 2018
Coding in Kotlin with Arrow NIDC 2018Coding in Kotlin with Arrow NIDC 2018
Coding in Kotlin with Arrow NIDC 2018Garth Gilmour
 
The Arrow Library in Kotlin
The Arrow Library in KotlinThe Arrow Library in Kotlin
The Arrow Library in KotlinGarth Gilmour
 
Python and sysadmin I
Python and sysadmin IPython and sysadmin I
Python and sysadmin IGuixing Bai
 
The Sincerest Form of Flattery
The Sincerest Form of FlatteryThe Sincerest Form of Flattery
The Sincerest Form of FlatteryJosé Paumard
 
Python 101++: Let's Get Down to Business!
Python 101++: Let's Get Down to Business!Python 101++: Let's Get Down to Business!
Python 101++: Let's Get Down to Business!Paige Bailey
 
The Sincerest Form of Flattery
The Sincerest Form of FlatteryThe Sincerest Form of Flattery
The Sincerest Form of FlatteryJosé Paumard
 
An Intro to Python in 30 minutes
An Intro to Python in 30 minutesAn Intro to Python in 30 minutes
An Intro to Python in 30 minutesSumit Raj
 
Learn python - for beginners - part-2
Learn python - for beginners - part-2Learn python - for beginners - part-2
Learn python - for beginners - part-2RajKumar Rampelli
 
FUNDAMENTALS OF PYTHON LANGUAGE
 FUNDAMENTALS OF PYTHON LANGUAGE  FUNDAMENTALS OF PYTHON LANGUAGE
FUNDAMENTALS OF PYTHON LANGUAGE Saraswathi Murugan
 
Python language data types
Python language data typesPython language data types
Python language data typesHoang Nguyen
 
Introduction to Python - Part Three
Introduction to Python - Part ThreeIntroduction to Python - Part Three
Introduction to Python - Part Threeamiable_indian
 
Introduction to advanced python
Introduction to advanced pythonIntroduction to advanced python
Introduction to advanced pythonCharles-Axel Dein
 

Mais procurados (20)

String in python use of split method
String in python use of split methodString in python use of split method
String in python use of split method
 
The Swift Compiler and Standard Library
The Swift Compiler and Standard LibraryThe Swift Compiler and Standard Library
The Swift Compiler and Standard Library
 
Learn python in 20 minutes
Learn python in 20 minutesLearn python in 20 minutes
Learn python in 20 minutes
 
Logic programming a ruby perspective
Logic programming a ruby perspectiveLogic programming a ruby perspective
Logic programming a ruby perspective
 
Coding in Kotlin with Arrow NIDC 2018
Coding in Kotlin with Arrow NIDC 2018Coding in Kotlin with Arrow NIDC 2018
Coding in Kotlin with Arrow NIDC 2018
 
The Arrow Library in Kotlin
The Arrow Library in KotlinThe Arrow Library in Kotlin
The Arrow Library in Kotlin
 
Python and sysadmin I
Python and sysadmin IPython and sysadmin I
Python and sysadmin I
 
python.ppt
python.pptpython.ppt
python.ppt
 
The Sincerest Form of Flattery
The Sincerest Form of FlatteryThe Sincerest Form of Flattery
The Sincerest Form of Flattery
 
Python 101++: Let's Get Down to Business!
Python 101++: Let's Get Down to Business!Python 101++: Let's Get Down to Business!
Python 101++: Let's Get Down to Business!
 
The Sincerest Form of Flattery
The Sincerest Form of FlatteryThe Sincerest Form of Flattery
The Sincerest Form of Flattery
 
An Intro to Python in 30 minutes
An Intro to Python in 30 minutesAn Intro to Python in 30 minutes
An Intro to Python in 30 minutes
 
Learn python - for beginners - part-2
Learn python - for beginners - part-2Learn python - for beginners - part-2
Learn python - for beginners - part-2
 
FUNDAMENTALS OF PYTHON LANGUAGE
 FUNDAMENTALS OF PYTHON LANGUAGE  FUNDAMENTALS OF PYTHON LANGUAGE
FUNDAMENTALS OF PYTHON LANGUAGE
 
Python
PythonPython
Python
 
Strings in python
Strings in pythonStrings in python
Strings in python
 
Python language data types
Python language data typesPython language data types
Python language data types
 
Introduction to Python - Part Three
Introduction to Python - Part ThreeIntroduction to Python - Part Three
Introduction to Python - Part Three
 
Free your lambdas
Free your lambdasFree your lambdas
Free your lambdas
 
Introduction to advanced python
Introduction to advanced pythonIntroduction to advanced python
Introduction to advanced python
 

Destaque

Busting the Field of Dreams Theory: Making Content Meaningful, Useful, and Fi...
Busting the Field of Dreams Theory: Making Content Meaningful, Useful, and Fi...Busting the Field of Dreams Theory: Making Content Meaningful, Useful, and Fi...
Busting the Field of Dreams Theory: Making Content Meaningful, Useful, and Fi...Blend Interactive
 
Content Heavy Lifting: How to Recycle (and Upcycle) Your Content Over and Ove...
Content Heavy Lifting: How to Recycle (and Upcycle) Your Content Over and Ove...Content Heavy Lifting: How to Recycle (and Upcycle) Your Content Over and Ove...
Content Heavy Lifting: How to Recycle (and Upcycle) Your Content Over and Ove...Blend Interactive
 
The Lone Ranger: Managing the Ups and Downs of One-Person Offices and Small T...
The Lone Ranger: Managing the Ups and Downs of One-Person Offices and Small T...The Lone Ranger: Managing the Ups and Downs of One-Person Offices and Small T...
The Lone Ranger: Managing the Ups and Downs of One-Person Offices and Small T...Blend Interactive
 
“The Five Meetings You Meet in Web Design” by Kevin Hoffman (Now What? Confer...
“The Five Meetings You Meet in Web Design” by Kevin Hoffman (Now What? Confer...“The Five Meetings You Meet in Web Design” by Kevin Hoffman (Now What? Confer...
“The Five Meetings You Meet in Web Design” by Kevin Hoffman (Now What? Confer...Blend Interactive
 
Next Level Collaboration: The Future of Content and Design by Rebekah Cancino...
Next Level Collaboration: The Future of Content and Design by Rebekah Cancino...Next Level Collaboration: The Future of Content and Design by Rebekah Cancino...
Next Level Collaboration: The Future of Content and Design by Rebekah Cancino...Blend Interactive
 
Reducing Digital Clutter: How to Clean Up the Back of Your House by Matt Groc...
Reducing Digital Clutter: How to Clean Up the Back of Your House by Matt Groc...Reducing Digital Clutter: How to Clean Up the Back of Your House by Matt Groc...
Reducing Digital Clutter: How to Clean Up the Back of Your House by Matt Groc...Blend Interactive
 
"The Self-Directed Strategist: Building a Practice and Managing Organizationa...
"The Self-Directed Strategist: Building a Practice and Managing Organizationa..."The Self-Directed Strategist: Building a Practice and Managing Organizationa...
"The Self-Directed Strategist: Building a Practice and Managing Organizationa...Blend Interactive
 
“Writing for Your Audience — The Message, the Words, the Plan” – Business Sen...
“Writing for Your Audience — The Message, the Words, the Plan” – Business Sen...“Writing for Your Audience — The Message, the Words, the Plan” – Business Sen...
“Writing for Your Audience — The Message, the Words, the Plan” – Business Sen...Blend Interactive
 
Object Oriented Programming with Java
Object Oriented Programming with JavaObject Oriented Programming with Java
Object Oriented Programming with JavaJussi Pohjolainen
 
Java Basics for selenium
Java Basics for seleniumJava Basics for selenium
Java Basics for seleniumapoorvams
 

Destaque (11)

C# concepts
C# conceptsC# concepts
C# concepts
 
Busting the Field of Dreams Theory: Making Content Meaningful, Useful, and Fi...
Busting the Field of Dreams Theory: Making Content Meaningful, Useful, and Fi...Busting the Field of Dreams Theory: Making Content Meaningful, Useful, and Fi...
Busting the Field of Dreams Theory: Making Content Meaningful, Useful, and Fi...
 
Content Heavy Lifting: How to Recycle (and Upcycle) Your Content Over and Ove...
Content Heavy Lifting: How to Recycle (and Upcycle) Your Content Over and Ove...Content Heavy Lifting: How to Recycle (and Upcycle) Your Content Over and Ove...
Content Heavy Lifting: How to Recycle (and Upcycle) Your Content Over and Ove...
 
The Lone Ranger: Managing the Ups and Downs of One-Person Offices and Small T...
The Lone Ranger: Managing the Ups and Downs of One-Person Offices and Small T...The Lone Ranger: Managing the Ups and Downs of One-Person Offices and Small T...
The Lone Ranger: Managing the Ups and Downs of One-Person Offices and Small T...
 
“The Five Meetings You Meet in Web Design” by Kevin Hoffman (Now What? Confer...
“The Five Meetings You Meet in Web Design” by Kevin Hoffman (Now What? Confer...“The Five Meetings You Meet in Web Design” by Kevin Hoffman (Now What? Confer...
“The Five Meetings You Meet in Web Design” by Kevin Hoffman (Now What? Confer...
 
Next Level Collaboration: The Future of Content and Design by Rebekah Cancino...
Next Level Collaboration: The Future of Content and Design by Rebekah Cancino...Next Level Collaboration: The Future of Content and Design by Rebekah Cancino...
Next Level Collaboration: The Future of Content and Design by Rebekah Cancino...
 
Reducing Digital Clutter: How to Clean Up the Back of Your House by Matt Groc...
Reducing Digital Clutter: How to Clean Up the Back of Your House by Matt Groc...Reducing Digital Clutter: How to Clean Up the Back of Your House by Matt Groc...
Reducing Digital Clutter: How to Clean Up the Back of Your House by Matt Groc...
 
"The Self-Directed Strategist: Building a Practice and Managing Organizationa...
"The Self-Directed Strategist: Building a Practice and Managing Organizationa..."The Self-Directed Strategist: Building a Practice and Managing Organizationa...
"The Self-Directed Strategist: Building a Practice and Managing Organizationa...
 
“Writing for Your Audience — The Message, the Words, the Plan” – Business Sen...
“Writing for Your Audience — The Message, the Words, the Plan” – Business Sen...“Writing for Your Audience — The Message, the Words, the Plan” – Business Sen...
“Writing for Your Audience — The Message, the Words, the Plan” – Business Sen...
 
Object Oriented Programming with Java
Object Oriented Programming with JavaObject Oriented Programming with Java
Object Oriented Programming with Java
 
Java Basics for selenium
Java Basics for seleniumJava Basics for selenium
Java Basics for selenium
 

Semelhante a Functional concepts in C#

Tuga IT 2017 - What's new in C# 7
Tuga IT 2017 - What's new in C# 7Tuga IT 2017 - What's new in C# 7
Tuga IT 2017 - What's new in C# 7Paulo Morgado
 
Clone Refactoring with Lambda Expressions
Clone Refactoring with Lambda ExpressionsClone Refactoring with Lambda Expressions
Clone Refactoring with Lambda ExpressionsNikolaos Tsantalis
 
devLink - What's New in C# 4?
devLink - What's New in C# 4?devLink - What's New in C# 4?
devLink - What's New in C# 4?Kevin Pilch
 
C# 6 and 7 and Futures 20180607
C# 6 and 7 and Futures 20180607C# 6 and 7 and Futures 20180607
C# 6 and 7 and Futures 20180607Kevin Hazzard
 
Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#Mark Needham
 
Intel JIT Talk
Intel JIT TalkIntel JIT Talk
Intel JIT Talkiamdvander
 
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...tdc-globalcode
 
Introduction to clojure
Introduction to clojureIntroduction to clojure
Introduction to clojureAbbas Raza
 
Functional programming ii
Functional programming iiFunctional programming ii
Functional programming iiPrashant Kalkar
 
To Infinity & Beyond: Protocols & sequences in Node - Part 2
To Infinity & Beyond: Protocols & sequences in Node - Part 2To Infinity & Beyond: Protocols & sequences in Node - Part 2
To Infinity & Beyond: Protocols & sequences in Node - Part 2Bahul Neel Upadhyaya
 
Lambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive CodeLambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive CodeIan Robertson
 
Impala: A Modern, Open-Source SQL Engine for Hadoop
Impala: A Modern, Open-Source SQL Engine for HadoopImpala: A Modern, Open-Source SQL Engine for Hadoop
Impala: A Modern, Open-Source SQL Engine for HadoopAll Things Open
 
Ast transformations
Ast transformationsAst transformations
Ast transformationsHamletDRC
 

Semelhante a Functional concepts in C# (20)

Tuga IT 2017 - What's new in C# 7
Tuga IT 2017 - What's new in C# 7Tuga IT 2017 - What's new in C# 7
Tuga IT 2017 - What's new in C# 7
 
What's New In C# 7
What's New In C# 7What's New In C# 7
What's New In C# 7
 
Poly-paradigm Java
Poly-paradigm JavaPoly-paradigm Java
Poly-paradigm Java
 
Clone Refactoring with Lambda Expressions
Clone Refactoring with Lambda ExpressionsClone Refactoring with Lambda Expressions
Clone Refactoring with Lambda Expressions
 
devLink - What's New in C# 4?
devLink - What's New in C# 4?devLink - What's New in C# 4?
devLink - What's New in C# 4?
 
C# 6 and 7 and Futures 20180607
C# 6 and 7 and Futures 20180607C# 6 and 7 and Futures 20180607
C# 6 and 7 and Futures 20180607
 
Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#
 
Intel JIT Talk
Intel JIT TalkIntel JIT Talk
Intel JIT Talk
 
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
 
Introduction to clojure
Introduction to clojureIntroduction to clojure
Introduction to clojure
 
Functional programming ii
Functional programming iiFunctional programming ii
Functional programming ii
 
To Infinity & Beyond: Protocols & sequences in Node - Part 2
To Infinity & Beyond: Protocols & sequences in Node - Part 2To Infinity & Beyond: Protocols & sequences in Node - Part 2
To Infinity & Beyond: Protocols & sequences in Node - Part 2
 
core java
 core java core java
core java
 
C tutorial
C tutorialC tutorial
C tutorial
 
C tutorial
C tutorialC tutorial
C tutorial
 
C tutorial
C tutorialC tutorial
C tutorial
 
Lambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive CodeLambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive Code
 
Python basic
Python basicPython basic
Python basic
 
Impala: A Modern, Open-Source SQL Engine for Hadoop
Impala: A Modern, Open-Source SQL Engine for HadoopImpala: A Modern, Open-Source SQL Engine for Hadoop
Impala: A Modern, Open-Source SQL Engine for Hadoop
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
 

Mais de Blend Interactive

"The Accessible Editor Workshop" by Corey Vilhauer, from Now What? Workshops ...
"The Accessible Editor Workshop" by Corey Vilhauer, from Now What? Workshops ..."The Accessible Editor Workshop" by Corey Vilhauer, from Now What? Workshops ...
"The Accessible Editor Workshop" by Corey Vilhauer, from Now What? Workshops ...Blend Interactive
 
"The Accessible Editor" by Corey Vilhauer, from DrupalCon 2018 in Nashville, ...
"The Accessible Editor" by Corey Vilhauer, from DrupalCon 2018 in Nashville, ..."The Accessible Editor" by Corey Vilhauer, from DrupalCon 2018 in Nashville, ...
"The Accessible Editor" by Corey Vilhauer, from DrupalCon 2018 in Nashville, ...Blend Interactive
 
"Click to Continue" by Sam Otis, from Content+Design Meetup, Oct. 4, 2017
"Click to Continue" by Sam Otis, from Content+Design Meetup, Oct. 4, 2017"Click to Continue" by Sam Otis, from Content+Design Meetup, Oct. 4, 2017
"Click to Continue" by Sam Otis, from Content+Design Meetup, Oct. 4, 2017Blend Interactive
 
"Never Knowing Enough: dealing with the self doubt that hinders your success....
"Never Knowing Enough: dealing with the self doubt that hinders your success...."Never Knowing Enough: dealing with the self doubt that hinders your success....
"Never Knowing Enough: dealing with the self doubt that hinders your success....Blend Interactive
 
"Making things real: Content strategy for realistic content management" - Con...
"Making things real: Content strategy for realistic content management" - Con..."Making things real: Content strategy for realistic content management" - Con...
"Making things real: Content strategy for realistic content management" - Con...Blend Interactive
 
"Making things real: Content strategy for realistic content management" - Con...
"Making things real: Content strategy for realistic content management" - Con..."Making things real: Content strategy for realistic content management" - Con...
"Making things real: Content strategy for realistic content management" - Con...Blend Interactive
 
Getting Started With User-Centered Content by Emileigh Barnes & Kate Garklavs...
Getting Started With User-Centered Content by Emileigh Barnes & Kate Garklavs...Getting Started With User-Centered Content by Emileigh Barnes & Kate Garklavs...
Getting Started With User-Centered Content by Emileigh Barnes & Kate Garklavs...Blend Interactive
 
Content Measurement and Analytics: Making Positive Change on the Web by Rick ...
Content Measurement and Analytics: Making Positive Change on the Web by Rick ...Content Measurement and Analytics: Making Positive Change on the Web by Rick ...
Content Measurement and Analytics: Making Positive Change on the Web by Rick ...Blend Interactive
 
“How Silos Learn: Working in the Idea Factory” by Amanda Costello - Now What?...
“How Silos Learn: Working in the Idea Factory” by Amanda Costello - Now What?...“How Silos Learn: Working in the Idea Factory” by Amanda Costello - Now What?...
“How Silos Learn: Working in the Idea Factory” by Amanda Costello - Now What?...Blend Interactive
 
"Adaptive Content, Context, and Controversy
"Adaptive Content, Context, and Controversy"Adaptive Content, Context, and Controversy
"Adaptive Content, Context, and ControversyBlend Interactive
 
"Empathy Behind the Algorithms" by Chris Corak - Now What? Conference 2017
"Empathy Behind the Algorithms" by Chris Corak - Now What? Conference 2017"Empathy Behind the Algorithms" by Chris Corak - Now What? Conference 2017
"Empathy Behind the Algorithms" by Chris Corak - Now What? Conference 2017Blend Interactive
 
“The Beauty of Brevity” by Ravi Jain - Now What? Conference 2017
“The Beauty of Brevity” by Ravi Jain - Now What? Conference 2017“The Beauty of Brevity” by Ravi Jain - Now What? Conference 2017
“The Beauty of Brevity” by Ravi Jain - Now What? Conference 2017Blend Interactive
 
“Why Content Projects Fail” by Deane Barker - Now What? Conference 2017
“Why Content Projects Fail” by Deane Barker - Now What? Conference 2017“Why Content Projects Fail” by Deane Barker - Now What? Conference 2017
“Why Content Projects Fail” by Deane Barker - Now What? Conference 2017Blend Interactive
 
Not Perfect, "Always Better: A Story of Inclusion" by Derek Featherstone - No...
Not Perfect, "Always Better: A Story of Inclusion" by Derek Featherstone - No...Not Perfect, "Always Better: A Story of Inclusion" by Derek Featherstone - No...
Not Perfect, "Always Better: A Story of Inclusion" by Derek Featherstone - No...Blend Interactive
 
Content Design for Mobile Devices - Now What? Conference 2017
Content Design for Mobile Devices - Now What? Conference 2017Content Design for Mobile Devices - Now What? Conference 2017
Content Design for Mobile Devices - Now What? Conference 2017Blend Interactive
 
"Making Things Real: Taking content strategy from abstract to functional" - M...
"Making Things Real: Taking content strategy from abstract to functional" - M..."Making Things Real: Taking content strategy from abstract to functional" - M...
"Making Things Real: Taking content strategy from abstract to functional" - M...Blend Interactive
 
"Small CS: A Shoestring Approach to Content Strategy" by Corey Vilhauer at Co...
"Small CS: A Shoestring Approach to Content Strategy" by Corey Vilhauer at Co..."Small CS: A Shoestring Approach to Content Strategy" by Corey Vilhauer at Co...
"Small CS: A Shoestring Approach to Content Strategy" by Corey Vilhauer at Co...Blend Interactive
 
Introducing the Blend Web Operations Framework by Deane Barker (Now What? Con...
Introducing the Blend Web Operations Framework by Deane Barker (Now What? Con...Introducing the Blend Web Operations Framework by Deane Barker (Now What? Con...
Introducing the Blend Web Operations Framework by Deane Barker (Now What? Con...Blend Interactive
 
Living With Agile by Karl Fast (Now What? Conference 2015)
Living With Agile by Karl Fast (Now What? Conference 2015)Living With Agile by Karl Fast (Now What? Conference 2015)
Living With Agile by Karl Fast (Now What? Conference 2015)Blend Interactive
 
Integrating Accessibility: Planning Content for Everyone by Eileen Webb (Now ...
Integrating Accessibility: Planning Content for Everyone by Eileen Webb (Now ...Integrating Accessibility: Planning Content for Everyone by Eileen Webb (Now ...
Integrating Accessibility: Planning Content for Everyone by Eileen Webb (Now ...Blend Interactive
 

Mais de Blend Interactive (20)

"The Accessible Editor Workshop" by Corey Vilhauer, from Now What? Workshops ...
"The Accessible Editor Workshop" by Corey Vilhauer, from Now What? Workshops ..."The Accessible Editor Workshop" by Corey Vilhauer, from Now What? Workshops ...
"The Accessible Editor Workshop" by Corey Vilhauer, from Now What? Workshops ...
 
"The Accessible Editor" by Corey Vilhauer, from DrupalCon 2018 in Nashville, ...
"The Accessible Editor" by Corey Vilhauer, from DrupalCon 2018 in Nashville, ..."The Accessible Editor" by Corey Vilhauer, from DrupalCon 2018 in Nashville, ...
"The Accessible Editor" by Corey Vilhauer, from DrupalCon 2018 in Nashville, ...
 
"Click to Continue" by Sam Otis, from Content+Design Meetup, Oct. 4, 2017
"Click to Continue" by Sam Otis, from Content+Design Meetup, Oct. 4, 2017"Click to Continue" by Sam Otis, from Content+Design Meetup, Oct. 4, 2017
"Click to Continue" by Sam Otis, from Content+Design Meetup, Oct. 4, 2017
 
"Never Knowing Enough: dealing with the self doubt that hinders your success....
"Never Knowing Enough: dealing with the self doubt that hinders your success...."Never Knowing Enough: dealing with the self doubt that hinders your success....
"Never Knowing Enough: dealing with the self doubt that hinders your success....
 
"Making things real: Content strategy for realistic content management" - Con...
"Making things real: Content strategy for realistic content management" - Con..."Making things real: Content strategy for realistic content management" - Con...
"Making things real: Content strategy for realistic content management" - Con...
 
"Making things real: Content strategy for realistic content management" - Con...
"Making things real: Content strategy for realistic content management" - Con..."Making things real: Content strategy for realistic content management" - Con...
"Making things real: Content strategy for realistic content management" - Con...
 
Getting Started With User-Centered Content by Emileigh Barnes & Kate Garklavs...
Getting Started With User-Centered Content by Emileigh Barnes & Kate Garklavs...Getting Started With User-Centered Content by Emileigh Barnes & Kate Garklavs...
Getting Started With User-Centered Content by Emileigh Barnes & Kate Garklavs...
 
Content Measurement and Analytics: Making Positive Change on the Web by Rick ...
Content Measurement and Analytics: Making Positive Change on the Web by Rick ...Content Measurement and Analytics: Making Positive Change on the Web by Rick ...
Content Measurement and Analytics: Making Positive Change on the Web by Rick ...
 
“How Silos Learn: Working in the Idea Factory” by Amanda Costello - Now What?...
“How Silos Learn: Working in the Idea Factory” by Amanda Costello - Now What?...“How Silos Learn: Working in the Idea Factory” by Amanda Costello - Now What?...
“How Silos Learn: Working in the Idea Factory” by Amanda Costello - Now What?...
 
"Adaptive Content, Context, and Controversy
"Adaptive Content, Context, and Controversy"Adaptive Content, Context, and Controversy
"Adaptive Content, Context, and Controversy
 
"Empathy Behind the Algorithms" by Chris Corak - Now What? Conference 2017
"Empathy Behind the Algorithms" by Chris Corak - Now What? Conference 2017"Empathy Behind the Algorithms" by Chris Corak - Now What? Conference 2017
"Empathy Behind the Algorithms" by Chris Corak - Now What? Conference 2017
 
“The Beauty of Brevity” by Ravi Jain - Now What? Conference 2017
“The Beauty of Brevity” by Ravi Jain - Now What? Conference 2017“The Beauty of Brevity” by Ravi Jain - Now What? Conference 2017
“The Beauty of Brevity” by Ravi Jain - Now What? Conference 2017
 
“Why Content Projects Fail” by Deane Barker - Now What? Conference 2017
“Why Content Projects Fail” by Deane Barker - Now What? Conference 2017“Why Content Projects Fail” by Deane Barker - Now What? Conference 2017
“Why Content Projects Fail” by Deane Barker - Now What? Conference 2017
 
Not Perfect, "Always Better: A Story of Inclusion" by Derek Featherstone - No...
Not Perfect, "Always Better: A Story of Inclusion" by Derek Featherstone - No...Not Perfect, "Always Better: A Story of Inclusion" by Derek Featherstone - No...
Not Perfect, "Always Better: A Story of Inclusion" by Derek Featherstone - No...
 
Content Design for Mobile Devices - Now What? Conference 2017
Content Design for Mobile Devices - Now What? Conference 2017Content Design for Mobile Devices - Now What? Conference 2017
Content Design for Mobile Devices - Now What? Conference 2017
 
"Making Things Real: Taking content strategy from abstract to functional" - M...
"Making Things Real: Taking content strategy from abstract to functional" - M..."Making Things Real: Taking content strategy from abstract to functional" - M...
"Making Things Real: Taking content strategy from abstract to functional" - M...
 
"Small CS: A Shoestring Approach to Content Strategy" by Corey Vilhauer at Co...
"Small CS: A Shoestring Approach to Content Strategy" by Corey Vilhauer at Co..."Small CS: A Shoestring Approach to Content Strategy" by Corey Vilhauer at Co...
"Small CS: A Shoestring Approach to Content Strategy" by Corey Vilhauer at Co...
 
Introducing the Blend Web Operations Framework by Deane Barker (Now What? Con...
Introducing the Blend Web Operations Framework by Deane Barker (Now What? Con...Introducing the Blend Web Operations Framework by Deane Barker (Now What? Con...
Introducing the Blend Web Operations Framework by Deane Barker (Now What? Con...
 
Living With Agile by Karl Fast (Now What? Conference 2015)
Living With Agile by Karl Fast (Now What? Conference 2015)Living With Agile by Karl Fast (Now What? Conference 2015)
Living With Agile by Karl Fast (Now What? Conference 2015)
 
Integrating Accessibility: Planning Content for Everyone by Eileen Webb (Now ...
Integrating Accessibility: Planning Content for Everyone by Eileen Webb (Now ...Integrating Accessibility: Planning Content for Everyone by Eileen Webb (Now ...
Integrating Accessibility: Planning Content for Everyone by Eileen Webb (Now ...
 

Último

WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...HostedbyConfluent
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 

Último (20)

WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 

Functional concepts in C#

  • 1. Functional Concepts in C# Or “Who the F# Wrote This?” https://github.com/mrdrbob/sd-code-camp-2016
  • 4. What this talk is A gentle introduction to functional paradigms using a language you may already be familiar with. A comparison between OOP and functional styles A discussion on language expectations
  • 5. What this talk isn’t “OOP is dead!” “Functional all the things!” “All code should look exactly like this!” (Spoiler: it probably shouldn’t)
  • 6. Who I am Bob Davidson C# / Web Developer 11 years Blend Interactive A guy who is generally interested in and learning about functional programming concepts https://github.com/mrdrbob
  • 7. Who I am Not A functional programming expert who says things like: “All told, a monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.” -Saunders Mac Lane
  • 8. Let’s Build a Parser! A highly simplified JSON-like syntax for strings and integers.
  • 10. Strings Starts & ends with double quote. Quotes can be escaped with slash. Slash can be escaped with slash. Can be empty.
  • 12. The IParser<TValue> Interface public interface IParser<TValue>  { bool  TryParse(string raw,  out TValue value); }
  • 13. IntegerParser public class IntegerParser :  IParser<int>  { public bool  TryParse(string raw,  out int value)  { value  = 0; int x  = 0; List<char> buffer  = new List<char>(); while (x  < raw.Length && char.IsDigit(raw[x]))  { buffer.Add(raw[x]); x  += 1; } if (x  == 0) return false; //  Deal  with  it. value  = int.Parse(new string(buffer.ToArray())); return true; } }
  • 14. IntegerParser public class IntegerParser :  IParser<int>  { public bool  TryParse(string raw,  out int value)  { value  = 0; int x  = 0; List<char> buffer  = new List<char>(); while (x  < raw.Length && char.IsDigit(raw[x]))  { buffer.Add(raw[x]); x  += 1; } if (x  == 0) return false; value  = int.Parse(new string(buffer.ToArray())); return true; } }
  • 15. StringParser public class StringParser :  IParser<string>  { public bool  TryParse(string raw,  out string value)  { value  = null; int x  = 0; if (x  == raw.Length || raw[x]  != '"') return false; x  += 1; List<char> buffer  = new List<char>(); while (x  < raw.Length && raw[x]  != '"')  { if (raw[x]  == '')  { x  += 1; if (x  == raw.Length) return false; if (raw[x]  == '') buffer.Add(raw[x]); else if (raw[x]  == '"') buffer.Add(raw[x]); else return false; }  else { buffer.Add(raw[x]); } x  += 1; } if (x  == raw.Length) return false; x  += 1; value  = new string(buffer.ToArray()); return true; } }
  • 16. Possible Issues public class StringParser :  IParser<string>  { public bool  TryParse(string raw,  out string value)  { value  = null; int x  = 0; if (x  == raw.Length || raw[x]  != '"') return false; x  += 1; List<char> buffer  = new List<char>(); while (x  < raw.Length && raw[x]  != '"')  { if (raw[x]  == '')  { x  += 1; if (x  == raw.Length) return false; if (raw[x]  == '') buffer.Add(raw[x]); else if (raw[x]  == '"') buffer.Add(raw[x]); else return false; }  else { buffer.Add(raw[x]); } x  += 1; } if (x  == raw.Length) return false; x  += 1; value  = new string(buffer.ToArray()); return true; } } Repeated checks against running out of input Easily missed logic for moving input forward No way to see how much input was consumed / how much is left Hard to understand at a glance what is happening public class IntegerParser :  IParser<int>  { public bool  TryParse(string raw,  out int value)  { value  = 0; int x  = 0; List<char> buffer  = new List<char>(); while (x  < raw.Length && char.IsDigit(raw[x]))  { buffer.Add(raw[x]); x  += 1; } if (x  == 0) return false; //  Deal  with  it. value  = int.Parse(new string(buffer.ToArray())); return true; } }
  • 17. Rethinking the Parser Make a little more generic / reusable Break the process down into a series of rules which can be composed to make new parsers from existing parsers Build a framework that doesn’t rely on strings, but rather a stream of tokens
  • 20. One or More Times A Parser Built on Rules (Integer Parser) [0-9]
  • 21. Ignore Latter Keep Latter Zero or More Times Any of these NotKeep Latter A Parser Built on Rules (String Parser) “ “ Keep Latter Any of these “ “
  • 22. A Set of Rules Match Quote Match Slash Match Digit Match Then Keep Match Then Ignore Match Any Match Zero or More Times Match One or More Times Not
  • 23. Rethinking the Source Handle tokens other than chars (such as byte streams, pre-lexed tokens, etc) Need the ability to continue parsing after a success Need the ability to reset after a failure
  • 24. Rethinking the Source public interface ISource<Token>  { Token Current {  get;  } bool  HasMore {  get;  } int CurrentIndex {  get;  } void Move(int index); } public class StringSource :  ISource<char>  { readonly  string  value; int index; public StringSource(string value)  {  this.value  = value;  } public char Current  => value[index]; public int CurrentIndex  => index; public bool  HasMore  => index  < value.Length; public void Move(int index)  =>  this.index  =  index; }
  • 25. Creating a Rule public interface IRule<Token,  TResult>  { bool  TryParse(ISource<Token> source,  out TResult result); }
  • 26. Char Matches... public class CharIsQuote :  IRule<char,  char>  { public bool  TryParse(ISource<char> source,  out char result)  { result  = default(char); if (!source.HasMore) return false; if (source.Current != '"') return false; result  = source.Current; source.Move(source.CurrentIndex + 1); return true; } } public class CharIs :  IRule<char,  char>  { readonly  char toMatch; public CharIs(char toMatch)  {  this.toMatch  = toMatch;  } public bool  TryParse(ISource<char> source,  out char result)  { result  = default(char); if (!source.HasMore) return false; if (source.Current != toMatch) return false; result  = source.Current; source.Move(source.CurrentIndex + 1); return true; } }
  • 27. Char Matches... public abstract class CharMatches :  IRule<char,  char>  { protected abstract bool  IsCharMatch(char c); public bool  TryParse(ISource<char> source,  out char result)  { result  = default(char); if (!source.HasMore) return false; if (!IsCharMatch(source.Current)) return false; result  = source.Current; source.Move(source.CurrentIndex + 1); return true; } } public class CharIsDigit :  CharMatches  { protected override  bool  IsCharMatch(char c)  =>  char.IsDigit(c); } public class CharIs :  CharMatches  { readonly  char toMatch; public CharIs(char toMatch)  {  this.toMatch  = toMatch;  } protected override  bool  IsCharMatch(char c)  =>  c  ==  toMatch; }
  • 28. First Match (or Any) public class FirstMatch<Token,  TResult>  :  IRule<Token,  TResult>  { readonly  IRule<Token,  TResult>[]  rules; public FirstMatch(IRule<Token,  TResult>[]  rules)  {  this.rules  = rules;  } public bool  TryParse(ISource<Token> source,  out TResult result)  { foreach(var  rule  in  rules)  { int originalIndex  = source.CurrentIndex; if (rule.TryParse(source,  out  result)) return true; source.Move(originalIndex); } result  = default(TResult); return false; } }
  • 29. Match Then... public abstract class MatchThen<Token,  TLeft,  TRight,  TOut>  :  IRule<Token,  TOut>  { readonly  IRule<Token,  TLeft> leftRule; readonly  IRule<Token,  TRight> rightRule; protected abstract TOut Combine(TLeft leftResult,  TRight rightResult); public MatchThen(IRule<Token,  TLeft> leftRule,  IRule<Token,  TRight> rightRule)  { this.leftRule  = leftRule; this.rightRule  = rightRule; } public bool  TryParse(ISource<Token> source,  out TOut result)  { int originalIndex  = source.CurrentIndex; result  = default(TOut); TLeft leftResult; if (!leftRule.TryParse(source,  out  leftResult))  { source.Move(originalIndex); return false; } TRight rightResult; if (!rightRule.TryParse(source,  out  rightResult))  { source.Move(originalIndex); return false; } result  = Combine(leftResult,  rightResult); return true; } }
  • 30. Match Then... public class MatchThenKeep<Token,  TLeft,  TRight>  :  MatchThen<Token,  TLeft,  TRight,  TRight>  { public MatchThenKeep(IRule<Token,  TLeft> leftRule,  IRule<Token,  TRight> rightRule)  :  base(leftRule,  rightRule)  {  } protected override  TRight Combine(TLeft leftResult,  TRight rightResult)  =>  rightResult; } public class MatchThenIgnore<Token,  TLeft,  TRight>  :  MatchThen<Token,  TLeft,  TRight,  TLeft>  { public MatchThenIgnore(IRule<Token,  TLeft> leftRule,  IRule<Token,  TRight> rightRule)  :  base(leftRule,  rightRule)  {  } protected override  TLeft Combine(TLeft leftResult,  TRight rightResult)  =>  leftResult; }
  • 31. Invert Rule (Not) public class Not<Token,  TResult>  :  IRule<Token,  Token>  { readonly  IRule<Token,  TResult> rule; public Not(IRule<Token,  TResult> rule)  {  this.rule  = rule;  } public bool  TryParse(ISource<Token> source,  out Token result)  { result  = default(Token); if (!source.HasMore) return false; int originalIndex  = source.CurrentIndex; TResult throwAwayResult; bool  matches  = rule.TryParse(source,  out  throwAwayResult); if (matches) { source.Move(originalIndex); return false; } source.Move(originalIndex); result  = source.Current; source.Move(originalIndex  + 1); return true; } } Spot the bug!
  • 32. Many (Once, Zero, and more times) public class Many<Token,  TResult>  :  IRule<Token,  TResult[]>  { readonly  IRule<Token,  TResult> rule; readonly  bool  requireAtLeastOne; public Many(IRule<Token,  TResult> rule,  bool requireAtLeastOne)  {  this.rule  = rule;  this.requireAtLeastOne  = requireAtLeastOne;  } public bool  TryParse(ISource<Token> source,  out TResult[]  results)  { List<TResult> buffer  = new List<TResult>(); while (source.HasMore)  { int originalIndex  = source.CurrentIndex; TResult result; bool  matched  = rule.TryParse(source,  out  result); if (!matched)  { source.Move(originalIndex); break; } buffer.Add(result); } if (requireAtLeastOne  && buffer.Count == 0)  { results  = null; return false; } results  = buffer.ToArray(); return true; } }
  • 33. Map Result public abstract class MapTo<Token,  TIn,  TOut>  :  IRule<Token,  TOut>  { readonly  IRule<Token,  TIn> rule; protected MapTo(IRule<Token,  TIn> rule)  {  this.rule  = rule;  } protected abstract TOut Convert(TIn value); public bool  TryParse(ISource<Token> source,  out TOut result)  { result  = default(TOut); int originalIndex  = source.CurrentIndex; TIn resultIn; if (!rule.TryParse(source,  out  resultIn))  { source.Move(originalIndex); return false; } result  = Convert(resultIn); return true; } }
  • 34. Map Result public class JoinText :  MapTo<char,  char[],  string>  { public JoinText(IRule<char,  char[]> rule)  :  base(rule)  {  } protected override  string  Convert(char[]  value)  =>  new  string(value); } public class MapToInteger :  MapTo<char,  string,  int>  { public MapToInteger(IRule<char,  string> rule)  :  base(rule)  {  } protected override  int Convert(string value)  =>  int.Parse(value); }
  • 35. Putting the blocks together var  quote  = new CharIs('"'); var  slash  = new CharIs(''); var  escapedQuote  = new MatchThenKeep<char,  char,  char>(slash,  quote); var  escapedSlash  = new MatchThenKeep<char,  char,  char>(slash,  slash); var  notQuote  = new Not<char,  char>(quote); var  insideQuoteChar  = new FirstMatch<char,  char>(new[]  { (IRule<char,  char>)escapedQuote, escapedSlash, notQuote }); var  insideQuote  = new Many<char,  char>(insideQuoteChar,  false); var  insideQuoteAsString  = new JoinText(insideQuote); var  openQuote  = new MatchThenKeep<char,  char,  string>(quote,   insideQuoteAsString); var  fullQuote  = new MatchThenIgnore<char,  string,  char>(openQuote,  quote); var  source  = new StringSource(raw); string  asQuote; if (fullQuote.TryParse(source,  out  asQuote)) return asQuote; source.Move(0); int asInteger; if (digitsAsInt.TryParse(source,  out  asInteger)) return asInteger; return null; var  digit  = new CharIsDigit(); var  digits  = new Many<char,  char>(digit,  true); var  digitsString  = new JoinText(digits); var  digitsAsInt  = new MapToInteger(digitsString);
  • 40. A Comparison (just the definition)
  • 41. Room for Improvement public abstract class MatchThen<Token,  TLeft,  TRight,  TOut>  :  IRule<Token,  TOut>  { readonly  IRule<Token,  TLeft> leftRule; readonly  IRule<Token,  TRight> rightRule; protected abstract TOut Combine(TLeft leftResult,  TRight rightResult); public MatchThen(IRule<Token,  TLeft> leftRule,  IRule<Token,  TRight> rightRule)  { this.leftRule  = leftRule; this.rightRule  = rightRule; } public bool  TryParse(ISource<Token> source,  out TOut result)  { int originalIndex  = source.CurrentIndex; result  = default(TOut); TLeft leftResult; if (!leftRule.TryParse(source,  out  leftResult))  { source.Move(originalIndex); return false; } TRight rightResult; if (!rightRule.TryParse(source,  out  rightResult))  { source.Move(originalIndex); return false; } result  = Combine(leftResult,  rightResult); return true; } } Out parameter :( Managing the source’s index
  • 43. An Immutable Source public interface ISource<Token>  { Token Current {  get;  } bool  HasMore {  get;  } int CurrentIndex {  get;  } void Move(int index); } public interface ISource<Token>  { Token Current {  get;  } bool  HasMore {  get;  } ISource<Token> Next(); }
  • 44. An Immutable Source public class StringSource :  ISource<char>  { readonly  string  value; int index; public StringSource(string value)  {   this.value  = value;  } public char Current  => value[index]; public int CurrentIndex  => index; public bool  HasMore  => index  < value.Length; public void Move(int index)  =>  this.index  =  index; } public class StringSource :  ISource<char>  { readonly  string  value; readonly  int index; public StringSource(string value,  int index =  0)  {   this.value  = value;  this.index  = index;  } public char Current  => value[index]; public bool  HasMore  => index  < value.Length; public ISource<char> Next()  =>   new  StringSource(value,  index +  1); }
  • 45. Ditch the Out public class Result<Token,  TValue>  { public bool  Success {  get;  } public TValue Value {  get;  } public string  Message {  get;  } public ISource<Token> Next {  get;  } public Result(bool success,  TValue value,  string message,  ISource<Token> next)  { Success = success; Value = value; Message = message; Next = next; } } public interface IRule<Token,  TValue>  { Result<Token,  TValue> TryParse(ISource<Token> source); }
  • 46. Char Matches... public abstract class CharMatches :  IRule<char,  char>  { protected abstract bool  IsCharMatch(char c); public bool  TryParse(ISource<char> source,  out char result)  { result  = default(char); if (!source.HasMore) return false; if (!IsCharMatch(source.Current)) return false; result  = source.Current; source.Move(source.CurrentIndex + 1); return true; } } public abstract class CharMatches :  IRule<char,  char>  { protected abstract bool  IsCharMatch(char c); public Result<char,  char> TryParse(ISource<char> source)  { if (!source.HasMore) return new Result<char,  char>(false,  '0',  "Unexpected  EOF",  null); if (!IsCharMatch(source.Current)) return new Result<char,  char>(false,  '0',  $"Unexpected  char:  {source.Current}",  null); return new Result<char,  char>(true,  source.Current,  null,  source.Next()); } }
  • 47. These Don’t Change public class CharIsDigit :  CharMatches  { protected override  bool  IsCharMatch(char c)  =>  char.IsDigit(c); } public class CharIs :  CharMatches  { readonly  char toMatch; public CharIs(char toMatch)  {  this.toMatch  = toMatch;  } protected override  bool  IsCharMatch(char c)  =>  c  ==  toMatch; }
  • 48. First Match public class FirstMatch<Token,  TResult>  :  IRule<Token,  TResult>  { readonly  IRule<Token,  TResult>[]  rules; public FirstMatch(IRule<Token,  TResult>[]  rules)  {  this.rules  = rules;  } public bool  TryParse(ISource<Token> source,  out TResult result)  { foreach(var  rule  in  rules)  { int originalIndex  = source.CurrentIndex; if (rule.TryParse(source,  out  result)) return true; source.Move(originalIndex); } result  = default(TResult); return false; } } public class FirstMatch<Token,  TResult>  :  IRule<Token,  TResult>  { readonly  IRule<Token,  TResult>[]  rules; public FirstMatch(IRule<Token,  TResult>[]  rules)  {  this.rules  = rules;  } public Result<Token,  TResult> TryParse(ISource<Token> source)  { foreach  (var  rule  in  rules)  { var  result  = rule.TryParse(source); if (result.Success) return result; } return new Result<Token,  TResult>(false,  default(TResult),  "No  rule  matched",  null); } }
  • 49. Match Then... public bool  TryParse(ISource<Token> source,  out TOut result)  { int originalIndex  = source.CurrentIndex; result  = default(TOut); TLeft leftResult; if (!leftRule.TryParse(source,  out  leftResult))  { source.Move(originalIndex); return false; } TRight rightResult; if (!rightRule.TryParse(source,  out  rightResult))  { source.Move(originalIndex); return false; } result  = Combine(leftResult,  rightResult); return true; } public Result<Token,  TOut> TryParse(ISource<Token> source)  { var  leftResult  = leftRule.TryParse(source); if (!leftResult.Success) return new Result<Token,  TOut>(false,  default(TOut),  leftResult.Message,  null); var  rightResult  = rightRule.TryParse(leftResult.Next); if (!rightResult.Success) return new Result<Token,  TOut>(false,  default(TOut),  rightResult.Message,  null); var  result  = Combine(leftResult.Value,  rightResult.Value); return new Result<Token,  TOut>(true,  result,  null,  rightResult.Next); }
  • 50. Invert Rule (Not) public class Not<Token,  TResult>  :  IRule<Token,  Token>  { readonly  IRule<Token,  TResult> rule; public Not(IRule<Token,  TResult> rule)  {  this.rule  = rule;  } public bool  TryParse(ISource<Token> source,  out Token result)  { result  = default(Token); if (!source.HasMore) return false; int originalIndex  = source.CurrentIndex; TResult throwAwayResult; bool  matches  = rule.TryParse(source,  out  throwAwayResult); if (matches) { source.Move(originalIndex); return false; } source.Move(originalIndex); result  = source.Current; source.Move(originalIndex  + 1); return true; } } public class Not<Token,  TResult>  :  IRule<Token,  Token>  { readonly  IRule<Token,  TResult> rule; public Not(IRule<Token,  TResult> rule)  {  this.rule  = rule;  } public Result<Token,  Token> TryParse(ISource<Token> source)  { if (!source.HasMore) return new Result<Token,  Token>(false,  default(Token),  "Unexpected   EOF",  null); var  result  = rule.TryParse(source); if (result.Success) return new Result<Token,  Token>(false,  default(Token),  "Unexpected   match",  null); return new Result<Token,  Token>(true,  source.Current,  null,   source.Next()); } }
  • 52. Still Room for Improvement public class Result<Token,  TValue>  { public bool  Success {  get;  } public TValue Value {  get;  } public string  Message {  get;  } public ISource<Token> Next {  get;  } public Result(bool success,  TValue value,  string message,  ISource<Token> next)  { Success = success; Value = value; Message = message; Next = next; } } Only valid when Success = true Only valid when Success = false
  • 53. Iteration 2.2 Discriminated Unions and Pattern Matching (sorta)
  • 54. Two States (Simple “Result” Example) public interface IResult {  } public class SuccessResult<TValue>  :  IResult  { public TValue Value {  get;  } public SuccessResult(TValue value)  {  Value = value;  } } public class ErrorResult :  IResult  { public string  Message {  get;  } public ErrorResult(string message)  {  Message = message;  } }
  • 55. Two States (The Matching) IResult result  = ParseIt(); if (result  is  SuccessResult<string>)  { var  success  = (SuccessResult<string>)result; Console.WriteLine($"SUCCESS:  {success.Value}"); }  else if (result  is  ErrorResult)  { var  error  = (ErrorResult)result; Console.WriteLine($"ERR:  {error.Message}"); }
  • 56. Pattern Matching(ish) public interface IResult<TValue>  { T  Match<T>(Func<SuccessResult<TValue>,  T> success, Func<ErrorResult<TValue>,  T> error); } public class SuccessResult<TValue>  :  IResult<TValue>  { public TValue Value {  get;  } public SuccessResult(TValue value)  {  Value = value;  } public T  Match<T>(Func<SuccessResult<TValue>,  T> success, Func<ErrorResult<TValue>,  T> error)  =>  success(this); } public class ErrorResult<TValue>  :  IResult<TValue> { public string  Message {  get;  } public ErrorResult(string message)  {  Message = message;  } public T  Match<T>(Func<SuccessResult<TValue>,  T> success, Func<ErrorResult<TValue>,  T> error)  =>  error(this); }
  • 57. Pattern Matching(ish) IResult<string> result  = ParseIt(); string  message  = result.Match( success  => $"SUCCESS:  ${success.Value}", error  => $"ERR:  {error.Message}"); Console.WriteLine(message); IResult result  = ParseIt(); if (result  is  SuccessResult<string>)  { var  success  = (SuccessResult<string>)result; Console.WriteLine($"SUCCESS:  {success.Value}"); }  else if (result  is  ErrorResult)  { var  error  = (ErrorResult)result; Console.WriteLine($"ERR:  {error.Message}"); }
  • 58. The Match Method Forces us to handle all cases Gives us an object with only valid properties for that state
  • 59. The New IResult public interface IResult<Token,  TValue>  { T  Match<T>(Func<FailResult<Token,  TValue>,  T> fail, Func<SuccessResult<Token,  TValue>,  T> success); } public class FailResult<Token,  TValue>  :  IResult<Token,  TValue>  { public string  Message {  get;  } public FailResult(string message)  {  Message = message;  } public T  Match<T>(Func<FailResult<Token,  TValue>,  T> fail, Func<SuccessResult<Token,  TValue>,  T> success)  =>  fail(this); } public class SuccessResult<Token,  TValue>  :  IResult<Token,  TValue>  { public TValue Value {  get;  } public ISource<Token> Next {  get;  } public SuccessResult(TValue value,  ISource<Token> next)  {  Value = value;  Next = next;  } public T  Match<T>(Func<FailResult<Token,  TValue>,  T> fail, Func<SuccessResult<Token,  TValue>,  T> success)  =>  success(this); }
  • 60. ISource also Represents Two States public interface ISource<Token>  { Token Current {  get;  } bool  HasMore {  get;  } ISource<Token> Next(); } Only valid when HasMore = true
  • 61. The New ISource public interface ISource<Token>  { T  Match<T>(Func<EmtySource<Token>,  T> empty, Func<SourceWithMoreContent<Token>,  T> hasMore); } public class EmtySource<Token>  :  ISource<Token>  { //  No  properties!    No  state!    Let's  just  make  it  singleton. EmtySource()  {  } public static readonly  EmtySource<Token> Instance  = new EmtySource<Token>(); public T  Match<T>(Func<EmtySource<Token>,  T> empty, Func<SourceWithMoreContent<Token>,  T> hasMore)  =>  empty(this); } public class SourceWithMoreContent<Token>  :  ISource<Token>  { readonly  Func<ISource<Token>> getNext; public SourceWithMoreContent(Token current,  Func<ISource<Token>> getNext)  {  Current = current;  this.getNext  = getNext;  } public Token Current {  get;  set;  } public ISource<Token> Next()  =>  getNext(); public T  Match<T>(Func<EmtySource<Token>,  T> empty, Func<SourceWithMoreContent<Token>,  T> hasMore)  =>  hasMore(this); }
  • 62. Make a String Source public static class StringSource { public static ISource<char> Create(string value,  int index =  0)  { if (index  >= value.Length) return EmtySource<char>.Instance; return new SourceWithMoreContent<char>(value[index],  ()  => Create(value,  index  + 1)); } } public static ISource<char> Create(string  value,  int index  = 0) => index  >= value.Length ? (ISource<char>)EmtySource<char>.Instance : new SourceWithMoreContent<char>(value[index],  ()  => Create(value,  index  + 1));
  • 63. Char Matches... public abstract class CharMatches :  IRule<char,  char>  { protected abstract bool  IsCharMatch(char c); public Result<char,  char> TryParse(ISource<char> source)  { if (!source.HasMore) return new Result<char,  char>(false,  '0',  "Unexpected  EOF",  null); if (!IsCharMatch(source.Current)) return new Result<char,  char>(false,  '0',  $"Unexpected  char:  {source.Current}",  null); return new Result<char,  char>(true,  source.Current,  null,  source.Next()); } } public abstract class CharMatches :  IRule<char,  char>  { protected abstract bool  IsCharMatch(char c); public IResult<char,  char> TryParse(ISource<char> source)  { var  result  = source.Match( empty  => (IResult<char,  char>)new FailResult<char,  char>("Unexpected  EOF"), hasMore  => { if (!IsCharMatch(hasMore.Current)) return new FailResult<char,  char>($"Unexpected  char:  {hasMore.Current}"); return new SuccessResult<char,  char>(hasMore.Current,  hasMore.Next()); }); return result; } } public IResult<char,  char> TryParse(ISource<char> source) => source.Match( empty  => new FailResult<char,  char>("Unexpected  EOF"), hasMore  => IsCharMatch(hasMore.Current) ? new SuccessResult<char,  char>(hasMore.Current,  hasMore.Next()) : (IResult<char,  char>)new FailResult<char,  char>($"Unexpected  char:  {hasMore.Current}") );
  • 64. Match Then... public IResult<Token,  TOut> TryParse(ISource<Token> source)  { var  leftResult  = leftRule.TryParse(source); var  finalResult  = leftResult.Match( leftFail  => new FailResult<Token,  TOut>(leftFail.Message), leftSuccess  => { var  rightResult  = rightRule.TryParse(leftSuccess.Next); var  rightFinalResult  = rightResult.Match( rightFail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(rightFail.Message), rightSuccess  => { var  finalValue  = Combine(leftSuccess.Value,  rightSuccess.Value); return new SuccessResult<Token,  TOut>(finalValue,  rightSuccess.Next); }); return rightFinalResult; }); return finalResult; } public Result<Token,  TOut> TryParse(ISource<Token> source)  { var  leftResult  = leftRule.TryParse(source); if (!leftResult.Success) return new Result<Token,  TOut>(false,  default(TOut),  leftResult.Message,  null); var  rightResult  = rightRule.TryParse(leftResult.Next); if (!rightResult.Success) return new Result<Token,  TOut>(false,  default(TOut),  rightResult.Message,  null); var  result  = Combine(leftResult.Value,  rightResult.Value); return new Result<Token,  TOut>(true,  result,  null,  rightResult.Next); } public IResult<Token,  TOut> TryParse(ISource<Token> source) => leftRule.TryParse(source).Match( leftFail  => new FailResult<Token,  TOut>(leftFail.Message), leftSuccess  => rightRule.TryParse(leftSuccess.Next).Match( rightFail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(rightFail.Message), rightSuccess  => new SuccessResult<Token,  TOut>(Combine(leftSuccess.Value,  rightSuccess.Value),   rightSuccess.Next) ) );
  • 65. Invert Rule (Not) public Result<Token,  Token> TryParse(ISource<Token> source)  { if (!source.HasMore) return new Result<Token,  Token>(false,  default(Token),  "Unexpected  EOF",  null); var  result  = rule.TryParse(source); if (result.Success) return new Result<Token,  Token>(false,  default(Token),  "Unexpected  match",  null); return new Result<Token,  Token>(true,  source.Current,  null,  source.Next()); } public IResult<Token,  Token> TryParse(ISource<Token> source) => source.Match( empty  => new FailResult<Token,  Token>("Unexpected  EOF"), current  => rule.TryParse(current).Match( fail  => new SuccessResult<Token,  Token>(current.Current,  current.Next()), success  => (IResult<Token,  Token>)new FailResult<Token,  Token>("Unexpected  match") ) );
  • 67. Let’s Be Honest All these `new` objects are ugly. var  quote  = new CharIs('"'); var  slash  = new CharIs(''); var  escapedQuote  = new MatchThenKeep<char,  char,   char>(slash,  quote); var  escapedSlash  = new MatchThenKeep<char,  char,   char>(slash,  slash); var  notQuote  = new Not<char,  char>(quote); var  insideQuoteChar  = new FirstMatch<char,  char>(new[]  { (IRule<char,  char>)escapedQuote, escapedSlash, notQuote }); var  insideQuote  = new Many<char,  char>(insideQuoteChar,   false); var  insideQuoteAsString  = new JoinText(insideQuote); var  openQuote  = new MatchThenKeep<char,  char,   string>(quote,  insideQuoteAsString); var  fullQuote  = new MatchThenIgnore<char,  string,   char>(openQuote,  quote);
  • 68. Also Single method interfaces are lame*. It’s effectively a delegate. public interface IRule<Token,  TValue>  { IResult<Token,  TValue> TryParse(ISource<Token> source); } *In  a  non-­scientific  poll  of  people  who  agree  with  me,  100%  of   respondents  confirmed  this  statement.    Do  not  question  its  validity.
  • 69. Iteration 3.0 Functions as First Class Citizens
  • 70. A Rule is a Delegate is a Function public interface IRule<Token,  TValue>  { IResult<Token,  TValue> TryParse(ISource<Token> source); } public delegate  IResult<Token,  TValue> Rule<Token,  TValue>(ISource<Token> source);
  • 71. Char Matches... public abstract class CharMatches :  IRule<char,  char>  { protected abstract bool  IsCharMatch(char c); public IResult<char,  char> TryParse(ISource<char> source) =>  source.Match( empty =>  new FailResult<char,  char>("Unexpected EOF"), hasMore  =>  IsCharMatch(hasMore.Current) ?  new  SuccessResult<char,  char>(hasMore.Current,  hasMore.Next()) :  (IResult<char,  char>)new  FailResult<char,  char>($"Unexpected  char:  {hasMore.Current}") ); } public static class Rules { public static Rule<char,  char> CharMatches(Func<char,  bool> isMatch) =>  (source)  =>  source.Match( empty =>  new FailResult<char,  char>("Unexpected EOF"), hasMore  =>  isMatch(hasMore.Current) ?  new  SuccessResult<char,  char>(hasMore.Current,  hasMore.Next()) :  (IResult<char,  char>)new  FailResult<char,  char>($"Unexpected  char:  {hasMore.Current}") ); } public static Rule<char,  char> CharIsDigit()  => CharMatches(char.IsDigit); public static Rule<char,  char> CharIs(char c)  => CharMatches(x  => x  == c);
  • 72. Then (Keep|Ignore) public static Rule<Token,  TOut> MatchThen<Token,  TLeft,  TRight,  TOut>(this Rule<Token,  TLeft> leftRule,  Rule<Token,   TRight> rightRule,  Func<TLeft,  TRight,  TOut> convert) => (source)  => leftRule(source).Match( leftFail  => new FailResult<Token,  TOut>(leftFail.Message), leftSuccess  => rightRule(leftSuccess.Next).Match( rightFail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(rightFail.Message), rightSuccess  => new SuccessResult<Token,  TOut>(convert(leftSuccess.Value,  rightSuccess.Value),   rightSuccess.Next) ) ); public static Rule<Token,  TRight> MatchThenKeep<Token,  TLeft,  TRight>(this Rule<Token,  TLeft> leftRule,  Rule<Token,   TRight> rightRule) => MatchThen(leftRule,  rightRule,  (left,  right)  => right); public static Rule<Token,  TLeft> MatchThenIgnore<Token,  TLeft,  TRight>(this Rule<Token,  TLeft> leftRule,  Rule<Token,   TRight> rightRule) => MatchThen(leftRule,  rightRule,  (left,  right)  => left);
  • 73. Not, MapTo, JoinText, MapToInteger public static Rule<Token,  Token> Not<Token,  TResult>(this Rule<Token,  TResult> rule) => (source)  => source.Match( empty  => new FailResult<Token,  Token>("Unexpected  EOF"), current  => rule(current).Match( fail  => new SuccessResult<Token,  Token>(current.Current,  current.Next()), success  => (IResult<Token,  Token>)new FailResult<Token,  Token>("Unexpected  match") ) ); public static Rule<Token,  TOut> MapTo<Token,  TIn,  TOut>(this Rule<Token,  TIn> rule,  Func<TIn,  TOut> convert) => (source)  => rule(source).Match( fail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(fail.Message), success  => new SuccessResult<Token,  TOut>(convert(success.Value),  success.Next) ); public static Rule<char,  string> JoinText(this Rule<char,  char[]> rule) => MapTo(rule,  (x)  => new string(x)); public static Rule<char,  int> MapToInteger(this Rule<char,  string> rule) => MapTo(rule,  (x)  => int.Parse(x));
  • 74. Example Usage var  quote  = Rules.CharIs('"'); var  slash  = Rules.CharIs(''); var  escapedQuote  =  Rules.MatchThenKeep(slash,  quote); var  escapedSlash  = slash.MatchThenKeep(slash);
  • 75. The Original 2.0 Definition var  quote  = new CharIs('"'); var  slash  = new CharIs(''); var  escapedQuote  = new MatchThenKeep<char,  char,  char>(slash,  quote); var  escapedSlash  = new MatchThenKeep<char,  char,  char>(slash,  slash); var  notQuote  = new Not<char,  char>(quote); var  insideQuoteChar  = new FirstMatch<char,  char>(new[]  { (IRule<char,  char>)escapedQuote, escapedSlash, notQuote }); var  insideQuote  = new Many<char,  char>(insideQuoteChar,  false); var  insideQuoteAsString  = new JoinText(insideQuote); var  openQuote  = new MatchThenKeep<char,  char,  string>(quote,   insideQuoteAsString); var  fullQuote  = new MatchThenIgnore<char,  string,  char>(openQuote,  quote); var  source  = new StringSource(raw); string  asQuote; if (fullQuote.TryParse(source,  out  asQuote)) return asQuote; source.Move(0); int asInteger; if (digitsAsInt.TryParse(source,  out  asInteger)) return asInteger; return null; var  digit  = new CharIsDigit(); var  digits  = new Many<char,  char>(digit,  true); var  digitsString  = new JoinText(digits); var  digitsAsInt  = new MapToInteger(digitsString);
  • 76. The Updated 3.0 Definition var  quote  = Rules.CharIs('"'); var  slash  = Rules.CharIs(''); var  escapedQuote  = slash.MatchThenKeep(quote); var  escapedSlash  = slash.MatchThenKeep(slash); var  notQuote  = quote.Not(); var  fullQuote  = quote .MatchThenKeep( Rules.FirstMatch( escapedQuote, escapedSlash, notQuote ).Many().JoinText() ) .MatchThenIgnore(quote); var  finalResult  = Rules.FirstMatch( fullQuote.MapTo(x  => (object)x), digit.MapTo(x  => (object)x) ); var  source  = StringSource.Create(raw); return finalResult(source).Match( fail  => null, success  => success.Value ); var  integer  = Rules.CharIsDigit() .Many(true) .JoinText() .MapToInteger();
  • 79. A Comparison V1 -> V3 (Just Definition)
  • 80. Looks Great! My co-workers are going to kill me
  • 81. Is it a good idea? public static Rule<Token,  Token> Not<Token,  TResult>(this Rule<Token,  TResult> rule) => (source)  => source.Match( empty  => new FailResult<Token,  Token>("Unexpected  EOF"), current  => rule(current).Match( fail  => new SuccessResult<Token,  Token>(current.Current,  current.Next()), success  => (IResult<Token,  Token>)new FailResult<Token,  Token>("Unexpected  match") ) ); public static Rule<Token,  TOut> MapTo<Token,  TIn,  TOut>(this Rule<Token,  TIn> rule,  Func<TIn,  TOut> convert) => (source)  => rule(source).Match( fail  => (IResult<Token,  TOut>)new FailResult<Token,  TOut>(fail.Message), success  => new SuccessResult<Token,  TOut>(convert(success.Value),  success.Next) ); public static Rule<char,  string> JoinText(this Rule<char,  char[]> rule) => MapTo(rule,  (x)  => new string(x)); public static Rule<char,  int> MapToInteger(this Rule<char,  string> rule) => MapTo(rule,  (x)  => int.Parse(x));
  • 82. Limitations “At Zombocom, the only limit… is yourself.” 1. Makes a LOT of short-lived objects (ISources, IResults). 2. As written currently, you will end up with the entire thing in memory. 3. Visual Studio’s Intellisense struggles with nested lambdas. 4. Frequently requires casts to solve type inference problems. 5. It’s not very C#.
  • 84. Iterations Iteration 1.0: Procedural Iteration 2.0: Making Compositional with OOP Iteration 2.1: Immutability Iteration 2.2: Discriminated Unions and Pattern Matching Iteration 3.0: Functions as First Class Citizens
  • 85. That’s All Thanks for coming and staying awake!