From 3390b7e5e4223f7e4871bb2fe0cd0e077115ced7 Mon Sep 17 00:00:00 2001 From: thautwarm Date: Wed, 26 Dec 2018 17:30:12 +0800 Subject: [PATCH] fix runtime and add guide --- FSTan.sln | 8 +++- FSTan/Data/List.fs | 4 +- FSTan/Data/Maybe.fs | 5 ++- FSTan/Functor.fs | 2 +- FSTan/HKT.fs | 14 +++++- FSTan/Monoid.fs | 1 + Guide.md | 87 ++++++++++++++++++++++++++++++++++++++ README.md | 4 ++ Tutorials/Program.fs | 62 +++++++++++++++++++++++++++ Tutorials/Tutorials.fsproj | 16 +++++++ 10 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 Guide.md create mode 100644 Tutorials/Program.fs create mode 100644 Tutorials/Tutorials.fsproj diff --git a/FSTan.sln b/FSTan.sln index f8cbca5..f5ab17a 100644 --- a/FSTan.sln +++ b/FSTan.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.28307.168 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSTan", "FSTan\FSTan.fsproj", "{79E22A01-CABB-4731-BE2C-652F6012304C}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSTan", "FSTan\FSTan.fsproj", "{79E22A01-CABB-4731-BE2C-652F6012304C}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tutorials", "Tutorials\Tutorials.fsproj", "{1A608D0F-5165-409B-B366-51302F216FD2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {79E22A01-CABB-4731-BE2C-652F6012304C}.Debug|Any CPU.Build.0 = Debug|Any CPU {79E22A01-CABB-4731-BE2C-652F6012304C}.Release|Any CPU.ActiveCfg = Release|Any CPU {79E22A01-CABB-4731-BE2C-652F6012304C}.Release|Any CPU.Build.0 = Release|Any CPU + {1A608D0F-5165-409B-B366-51302F216FD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A608D0F-5165-409B-B366-51302F216FD2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A608D0F-5165-409B-B366-51302F216FD2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A608D0F-5165-409B-B366-51302F216FD2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FSTan/Data/List.fs b/FSTan/Data/List.fs index 08cc158..3bfac1d 100644 --- a/FSTan/Data/List.fs +++ b/FSTan/Data/List.fs @@ -15,8 +15,8 @@ and HList() = override __.pure'<'a> (a: 'a) : hlist<'a> = wrap <| [a] - static member inline wrap<'a> (x : List<'a>): hlist<'a> = {wrap = x} :> _ - static member inline unwrap<'a> (x : hlist<'a>): List<'a> = (x :?> _).wrap + static member wrap<'a> (x : List<'a>): hlist<'a> = {wrap = x} :> _ + static member unwrap<'a> (x : hlist<'a>): List<'a> = (x :?> _).wrap and hListData<'a> = {wrap : List<'a>} diff --git a/FSTan/Data/Maybe.fs b/FSTan/Data/Maybe.fs index dc6e398..d12dc38 100644 --- a/FSTan/Data/Maybe.fs +++ b/FSTan/Data/Maybe.fs @@ -6,6 +6,7 @@ open FSTan.Monad type maybe<'a> = hkt and Maybe() = inherit monad() with + static member si = Maybe() override __.bind<'a, 'b> (m: maybe<'a>) (k: 'a -> maybe<'b>) = let m = unwrap m match m with @@ -13,8 +14,8 @@ and Maybe() = | None -> wrap None override __.pure'<'a> (a: 'a) : maybe<'a> = wrap <| Some a - static member inline wrap<'a> (x : Option<'a>): maybe<'a> = {wrap = x} :> _ - static member inline unwrap<'a> (x : maybe<'a>): Option<'a> = (x :?> _).wrap + static member wrap<'a> (x : Option<'a>): maybe<'a> = {wrap = x} :> _ + static member unwrap<'a> (x : maybe<'a>): Option<'a> = (x :?> _).wrap and MaybeData<'a> = {wrap : Option<'a>} diff --git a/FSTan/Functor.fs b/FSTan/Functor.fs index e1b66e4..a04d78b 100644 --- a/FSTan/Functor.fs +++ b/FSTan/Functor.fs @@ -3,7 +3,7 @@ open FSTan.HKT [] -type functor<'F>() = +type functor<'F>() = abstract member fmap<'a, 'b> : ('a -> 'b) -> hkt<'F, 'a> -> hkt<'F, 'b> abstract member ``<$``<'a, 'b> : 'a -> hkt<'F, 'b> -> hkt<'F, 'a> diff --git a/FSTan/HKT.fs b/FSTan/HKT.fs index 93e06b5..c88f675 100644 --- a/FSTan/HKT.fs +++ b/FSTan/HKT.fs @@ -1,8 +1,20 @@ module FSTan.HKT type hkt<'K, 'T> = interface end +open System +open System.Reflection + +let private ts = Array.zeroCreate 0 + +// seems to be silly... +// any default object? [] -let getsig<'a> = Unchecked.defaultof<'a> +let getsig<'a> = + let t = typeof<'a> + let f = t.GetConstructor(BindingFlags.Instance ||| BindingFlags.Public, null, + CallingConventions.HasThis, ts, null) + let o = f.Invoke([||]) + o :?> 'a // Some builtin data types like Map, List, Option cannot be interfaced // with `hkt`, so we have to wrap them. diff --git a/FSTan/Monoid.fs b/FSTan/Monoid.fs index dc6dd06..c2fd9ed 100644 --- a/FSTan/Monoid.fs +++ b/FSTan/Monoid.fs @@ -3,6 +3,7 @@ open FSTan.HKT [] type semigroup<'s>() = + abstract member op<'a> : hkt<'s, 'a> -> hkt<'s, 'a> -> hkt<'s, 'a> abstract member sconcat<'a> : diff --git a/Guide.md b/Guide.md new file mode 100644 index 0000000..f6eaa06 --- /dev/null +++ b/Guide.md @@ -0,0 +1,87 @@ +# FSTan Guide + + +Typeclasses +============= + + +Typeclassess are achived through abstract classes, which makes it works perfect for both subtypeclassing and default implementations. + +If some type is constructed with a type constructor, you can implement `show` class for it. + +Let's have a look at how to write a `show` class and use it in polymorphism functions and even operators. + + +```FSharp + +open FSTan.HKT + +// define a simple typeclass +[] +type show<'s>() = + abstract member show<'a> : hkt<'s, 'a> -> string + +// I have a typeclass, +// I have 2 datatypes, +// Oh! +// Polymorphism! +let show<'a, 's when 's :> show<'s>> = getsig<'s>.show<'a> + +type myData1<'a> = // define datatype + | A | B | C + interface hkt + +and MyTypeCons1() = + // define type constructor + // in F#, we don't really have this, but + // we can leverage a signature type(yes, this is just a signature) + // and `hkt`(check FSTan.HKT, not magic at all) + // to fully simulate a type constructor. + inherit show() with + override si.show a = + // This conversion can absolutely succeed + // for there is only one datatype which + // interfaces hkt + let a = a :?> _ myData1 + + sprintf "%A" a + + +type myData2<'a> = // define datatype + | I of int + | S of string + interface hkt +and MyTypeCons2() = + // define type constructor + // in F#, we don't really have this, but + // we can leverage a signature type(yes, this is just a signature) + // and `hkt`(check FSTan.HKT, not magic at all) + // to fully simulate a type constructor. + inherit show() with + override si.show a = + let a = a :?> _ myData2 + match a with + | I a -> sprintf "isInt %d" a + | S a -> sprintf "isStr %s" a +``` + + +Subtypeclassing +============= + +Check https://github.com/thautwarm/FSTan/blob/master/FSTan/Functor.fs. + + +Higher kined types +================== + +A signature type to represent a type constructor in FSTan: + +```FSharp +type Sig = .. + +let test_hkt<'a, 'b, 'c> (f: hkt<'a, 'b>) : hkt<'b, 'c> = + /// impl +``` + +In terms of above snippet, if `c` is a concrete type, then `'a` is kinded of `* -> * -> *`, as well as `b` is kinded of `* -> *`. \ No newline at end of file diff --git a/README.md b/README.md index 45fd606..b321660 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ Exactly a full-featured and practical implementation typeclasses and higher kinded types in F#. +For manuals check [Guide.md](https://github.com/thautwarm/FSTan/blob/master/Guide.md), where you'll be told how to use these concise typeclasses, higher kined types and constraints. + + + ## Motivation and Features There are also other similar implementations in FSharp like `Higher` and `FSharpPlus`, but they're not able to provide all the features listed below, which motivate me create a better one: diff --git a/Tutorials/Program.fs b/Tutorials/Program.fs new file mode 100644 index 0000000..e826b0a --- /dev/null +++ b/Tutorials/Program.fs @@ -0,0 +1,62 @@ +// Learn more about F# at http://fsharp.org + +open System + +open FSTan.HKT + +// define a simple typeclass +[] +type show<'s>() = + abstract member show<'a> : hkt<'s, 'a> -> string + +let show<'a, 's when 's :> show<'s>> = getsig<'s>.show<'a> + +type myData1<'a> = // define datatype + | A | B | C + interface hkt + +and MyTypeCons1() = + // define type constructor + // in F#, we don't really have this, but + // we can leverage a signature type(yes, this is just a signature) + // and `hkt`(check FSTan.HKT, not magic at all) + // to fully simulate a type constructor. + inherit show() with + override si.show a = + // This conversion can absolutely succeed + // for there is only one datatype which + // interfaces hkt + let a = a :?> _ myData1 + + sprintf "%A" a + + +type myData2<'a> = // define datatype + | I of int + | S of string + interface hkt +and MyTypeCons2() = + // define type constructor + // in F#, we don't really have this, but + // we can leverage a signature type(yes, this is just a signature) + // and `hkt`(check FSTan.HKT, not magic at all) + // to fully simulate a type constructor. + inherit show() with + override si.show a = + let a = a :?> _ myData2 + match a with + | I a -> sprintf "isInt %d" a + | S a -> sprintf "isStr %s" a + + +let test() = + let s1 = show <| I 32 + let s2 = show <| S "123" + let s3 = show A + let s4 = show B + printfn "%s\n%s\n%s\n%s" s1 s2 s3 s4 + +[] +let main argv = + test() + 0 // return an integer exit code diff --git a/Tutorials/Tutorials.fsproj b/Tutorials/Tutorials.fsproj new file mode 100644 index 0000000..11f2420 --- /dev/null +++ b/Tutorials/Tutorials.fsproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.1 + + + + + + + + + + +