Hi. I'm glad you like the series. The expression type discussed in my article is a sum type: Expr { Literal of int | Add of (Expr * Expr) }. You can see this in the F# implementation in my repository. So you can implement a fruit algebra the same way, like this:
using System;
namespace Fruit
{
/// <summary>
/// This represents the sum type. We can add new fruits later by
/// extending the interface.
/// </summary>
interface IFruitAlgebra<T>
{
T Banana();
T Orange();
T Apple();
}
/// <summary>
/// This represents the functions we can perform with the sum type.
/// We can add new behavior later by creating other interfaces.
/// </summary>
interface IFruitBehavior
{
string Slogan();
bool IsSlippery();
}
class FruitBehavior : IFruitBehavior
{
public FruitBehavior(Func<string> slogan, Func<bool> isSlippery)
{
_slogan = slogan;
_isSlippery = isSlippery;
}
Func<string> _slogan;
Func<bool> _isSlippery;
public string Slogan()
=> _slogan();
public bool IsSlippery()
=> _isSlippery();
}
class FruitAlgebra : IFruitAlgebra<IFruitBehavior>
{
public IFruitBehavior Banana()
=> new FruitBehavior(
() => "I like bananas!",
() => true);
public IFruitBehavior Orange()
=> new FruitBehavior(
() => "Oranges are best!",
() => false);
public IFruitBehavior Apple()
=> new FruitBehavior(
() => "Eat an apple!",
() => false);
}
static class FruitTest
{
public static T CreateTestFruit<T>(IFruitAlgebra<T> factory)
=> factory.Banana();
public static void RunTest()
{
Console.WriteLine("Fruit test:");
var fruit = CreateTestFruit(new FruitAlgebra());
Console.WriteLine($" Slogan: {fruit.Slogan()}");
Console.WriteLine($" Is slippery: {fruit.IsSlippery()}");
}
}
}
Hi! So grateful that you replied!
For some reason I'm still having a hard time extending the types of the example you provided, although I really like the example. Very clear and instructive.
I did something similar myself, with the goal of being able to extend types selectively, and I would love to hear your comments on the approach (heavily using the code you originally provided)
Especially if you have any thoughts on the downside of using this approach :)
What I think I've done is extensible types, extensible functionality, and it's all up to the caller to decide what to call. As well as there's only one generic method to get whatever we're interested in, from the objects that can provide it.
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;namespaceSumWithoutVisitor3{classProgram{staticvoidMain(string[]args){varrectangle=newRectangle(10,20);vartriangle=newTriangle(10,20,"A mighty fine triangle!");varcircle=newCircle(10);varareas=GetAnything<IShape,double,ICanDoSomeShapeCalculations<IShapeCalculationContract>>((ICanDoSomeShapeCalculations<IShapeCalculationContract>x)=>x.GetArea(),rectangle,triangle,circle);varcircumferences=GetAnything<IShape,double,ICanDoSomeMoreShapeCalculations<IShapeCalculationContract>>((ICanDoSomeMoreShapeCalculations<IShapeCalculationContract>x)=>x.GetCircumference(),rectangle,triangle,circle);vardescriptions=GetAnything<IShape,string,ICanDoSomeShapePrinting<IShapePrintingContract>>((ICanDoSomeShapePrinting<IShapePrintingContract>x)=>x.GetDescription(),rectangle,triangle,circle);Console.WriteLine("Areas");foreach(varainareas){Console.WriteLine(a.Item1+" : "+a.Item2);}Console.WriteLine("Circumferences");foreach(varcincircumferences){Console.WriteLine(c.Item1+" : "+c.Item2);}Console.WriteLine("Descriptions");foreach(vardindescriptions){Console.WriteLine(d.Item1+" : "+d.Item2);}}publicstaticIEnumerable<(string,R)>GetAnything<T,R,U>(Func<U,R>map,paramsT[]items){varanything=items.OfType<U>().Select(x=>(x.GetType().ToString(),map(x)));returnanything;}}interfaceIShape{}interfaceIShapeCalculationContract{doubleCalculate(Func<double>calculate);}// add new functionalityinterfaceIShapePrintingContract{stringPrint(Func<string>print);}classShape:IShapeCalculationContract,IShapePrintingContract{publicdoubleCalculate(Func<double>calculate)=>calculate();publicstringPrint(Func<string>print)=>print();}interfaceICanDoSomeShapeCalculations<T>:IShape{doubleGetArea();}interfaceICanDoSomeShapePrinting<T>:IShape{stringGetDescription();}classRectangle:ICanDoSomeShapeCalculations<IShapeCalculationContract>{publicdoubleWidth{get;}publicdoubleHeight{get;}publicRectangle(doublewidth,doubleheight){Width=width;Height=height;}publicdoubleGetArea()=>newShape().Calculate(()=>Width*Height);}classTriangle:ICanDoSomeShapeCalculations<IShapeCalculationContract>,ICanDoSomeShapePrinting<IShapePrintingContract>{publicstringDescription{get;}publicdoubleWidth{get;}publicdoubleHeight{get;}publicTriangle(doublewidth,doubleheight,stringdescription){Description=description;Width=width;Height=height;}publicdoubleGetArea()=>newShape().Calculate(()=>(Width*Height)/2);publicstringGetDescription()=>newShape().Print(()=>Description);}// add new method interfaceICanDoSomeMoreShapeCalculations<T>:ICanDoSomeShapeCalculations<T>{doubleGetCircumference();}// add new typeclassCircle:ICanDoSomeMoreShapeCalculations<IShapeCalculationContract>{publicdoubleRadius{get;}publicdoublePI{get;}publicCircle(doublea){Radius=a;PI=Math.PI;}publicdoubleGetArea()=>newShape().Calculate(()=>Radius*Radius*PI);publicdoubleGetCircumference()=>newShape().Calculate(()=>2*Radius*PI);}}
Output:
Areas
SumWithoutVisitor3.Rectangle : 200
SumWithoutVisitor3.Triangle : 100
SumWithoutVisitor3.Circle : 314,1592653589793
Circumferences
SumWithoutVisitor3.Circle : 62,83185307179586
Descriptions
SumWithoutVisitor3.Triangle : A mighty fine triangle!
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Hi. I'm glad you like the series. The expression type discussed in my article is a sum type:
Expr { Literal of int | Add of (Expr * Expr) }
. You can see this in the F# implementation in my repository. So you can implement a fruit algebra the same way, like this:Hi! So grateful that you replied!
For some reason I'm still having a hard time extending the types of the example you provided, although I really like the example. Very clear and instructive.
I did something similar myself, with the goal of being able to extend types selectively, and I would love to hear your comments on the approach (heavily using the code you originally provided)
Especially if you have any thoughts on the downside of using this approach :)
What I think I've done is extensible types, extensible functionality, and it's all up to the caller to decide what to call. As well as there's only one generic method to get whatever we're interested in, from the objects that can provide it.
Output:
Areas
SumWithoutVisitor3.Rectangle : 200
SumWithoutVisitor3.Triangle : 100
SumWithoutVisitor3.Circle : 314,1592653589793
Circumferences
SumWithoutVisitor3.Circle : 62,83185307179586
Descriptions
SumWithoutVisitor3.Triangle : A mighty fine triangle!