├── FSTan ├── Show.fs ├── MonadTrans.fs ├── Functor.fs ├── FSTan.fsproj ├── Data │ ├── Identity.fs │ ├── List.fs │ ├── Either.fs │ └── Maybe.fs ├── Monad.fs ├── Monoid.fs ├── HKT.fs ├── Applicative.fs └── Control │ ├── Trans │ └── State.fs │ └── State.fs ├── Tutorials ├── Tutorials.fsproj └── Program.fs ├── FSTan.sln ├── README.md ├── Guide.md ├── .gitignore └── LICENSE /FSTan/Show.fs: -------------------------------------------------------------------------------- 1 | module FSTan.Show 2 | open FSTan.HKT 3 | 4 | 5 | type show<'s> = 6 | interface 7 | abstract member show<'a> : hkt<'s, 'a> -> string 8 | end 9 | 10 | let show<'a, 's when 's :> show<'s>> (a: hkt<'s, 'a>) = getsig<'s>.show a -------------------------------------------------------------------------------- /FSTan/MonadTrans.fs: -------------------------------------------------------------------------------- 1 | module FSTan.MonadTrans 2 | open FSTan.HKT 3 | open FSTan.Monad 4 | 5 | type monadTrans<'t, 'm when 'm :> monad<'m>> = interface 6 | abstract lift<'a> : 7 | hkt<'m, 'a> -> hkt<'t, 'a> 8 | end 9 | 10 | let lift<'m, 'a, 't when 't :> monadTrans<'t, 'm>> = getsig<'t>.lift<'a> 11 | -------------------------------------------------------------------------------- /Tutorials/Tutorials.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /FSTan/Functor.fs: -------------------------------------------------------------------------------- 1 | module FSTan.Functor 2 | 3 | open FSTan.HKT 4 | 5 | [] 6 | type functor<'F>() = 7 | abstract member fmap<'a, 'b> : 8 | ('a -> 'b) -> hkt<'F, 'a> -> hkt<'F, 'b> 9 | abstract member ``<$``<'a, 'b> : 'a -> hkt<'F, 'b> -> hkt<'F, 'a> 10 | default si.``<$`` a b = 11 | let const' a _ = a 12 | (si.fmap << const') a b 13 | 14 | let fmap<'a, 'b, 'F when 'F :> functor<'F>> : 15 | ('a -> 'b) -> hkt<'F, 'a> -> hkt<'F, 'b> = 16 | getsig<'F>.fmap 17 | 18 | 19 | let ``<$``<'a, 'b, 'F when 'F :> functor<'F> > : 20 | 'a -> hkt<'F, 'b> -> hkt<'F, 'a> = getsig<'F>.``<$`` 21 | 22 | let (<<|) a b = ``<$`` a b -------------------------------------------------------------------------------- /FSTan/FSTan.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /FSTan/Data/Identity.fs: -------------------------------------------------------------------------------- 1 | module FSTan.Data.Identity 2 | 3 | open FSTan.HKT 4 | open FSTan.Monad 5 | open FSTan.Show 6 | 7 | type mkIdentity<'I>() = 8 | inherit monad>() 9 | override __.bind<'a, 'b> (m: hkt, 'a>) (k: 'a -> hkt, 'b>) = 10 | k (unwrap m) 11 | 12 | override __.pure'<'a> (a: 'a) : hkt, 'a> = wrap a 13 | static member wrap<'a> (x : 'a): hkt, 'a> = {wrap = x} :> _ 14 | static member unwrap<'a> (x : hkt, 'a>): 'a = (x :?> _).wrap 15 | interface show> with 16 | member __.show (x: hkt, 'a>) = 17 | let x = unwrap x 18 | x.ToString() 19 | 20 | and identityData<'I, 'a> = 21 | {wrap : 'a} 22 | interface hkt, 'a> 23 | 24 | -------------------------------------------------------------------------------- /FSTan/Data/List.fs: -------------------------------------------------------------------------------- 1 | module FSTan.Data.List 2 | 3 | open FSTan.HKT 4 | open FSTan.Monad 5 | open FSTan.Show 6 | 7 | module List' = List 8 | type List'<'a> = List<'a> 9 | 10 | type mkList<'L>() = 11 | inherit monad>() 12 | static member wrap<'a> (x : List'<'a>): hkt, 'a> = 13 | {wrap = x} :> _ 14 | static member unwrap<'a> (x : hkt, 'a>): List'<'a> = 15 | (x :?> _).wrap 16 | 17 | default si.bind<'a, 'b> (m: hkt, 'a>) (k: 'a -> hkt, 'b>): hkt, 'b> = 18 | wrap <| List'.collect (unwrap << k) (unwrap m) 19 | 20 | default si.pure'<'a> (a: 'a): hkt, 'a> = wrap <| [a] 21 | interface show> with 22 | member si.show (x: hkt, 'a>) = 23 | let x = unwrap x 24 | x.ToString() 25 | 26 | and listData<'L, 'a> = 27 | {wrap : List'<'a>} 28 | interface hkt, 'a> 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /FSTan/Data/Either.fs: -------------------------------------------------------------------------------- 1 | module FSTan.Data.Either 2 | open FSTan.HKT 3 | open FSTan.Show 4 | open FSTan.Monad 5 | 6 | type mkEither<'E, 'left>() = 7 | inherit monad>() 8 | default __.pure'<'a> (a: 'a): hkt, 'a> = Right a :> _ 9 | default __.bind<'a, 'b> (m: hkt, 'a>) (k: 'a -> hkt, 'b>): hkt, 'b> = 10 | match m :?> eitherData<'E, 'left, 'a> with 11 | | Left l -> Left l :> _ 12 | | Right r -> k r 13 | interface show> with 14 | member __.show<'a> (a: hkt, 'a>) = 15 | let a = a :?> eitherData<_, _, _> 16 | a.ToString() 17 | 18 | and eitherData<'E, 'e, 'a> = 19 | | Left of 'e 20 | | Right of 'a 21 | interface hkt, 'a> 22 | 23 | let Left<'E, 'e, 'a> (e: 'e) : hkt, 'a> = Left e :> _ 24 | let Right<'E, 'e, 'a> (a: 'a) : hkt, 'a> = Right a :> _ 25 | let (|Left|Right|) (m: hkt, 'a>) = 26 | match m :?> eitherData<'E, 'e, 'a> with 27 | | Left l -> Left l 28 | | Right r -> Right r 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /FSTan/Data/Maybe.fs: -------------------------------------------------------------------------------- 1 | module rec FSTan.Data.Maybe 2 | 3 | open FSTan.HKT 4 | open FSTan.Monad 5 | open FSTan.Show 6 | 7 | type mkMaybe<'M>() = 8 | inherit monad>() 9 | override __.bind<'a, 'b> (m: hkt, 'a>) (k: 'a -> hkt, 'b>) = 10 | match unwrap m with 11 | | Some a -> k a 12 | | None -> Nothing 13 | 14 | 15 | override __.pure'<'a> (a: 'a) : hkt, 'a> = wrap <| Some a 16 | static member wrap<'a> (x : Option<'a>): hkt, 'a> = {wrap = x} :> _ 17 | static member unwrap<'a> (x : hkt, 'a>): Option<'a> = (x :?> _).wrap 18 | interface show> with 19 | member __.show (a: hkt, 'a>) = 20 | let a = mkMaybe<'M>.unwrap a 21 | a.ToString() 22 | 23 | and OptionWrapper<'M, 'a> = 24 | {wrap : Option<'a>} 25 | interface hkt, 'a> 26 | 27 | let Just<'M, 'a> (a: 'a) : hkt, 'a> = wrap <| Some a 28 | 29 | [] 30 | let Nothing<'M, 'a> : hkt, 'a> = wrap <| None 31 | 32 | let (|Just|Nothing|) (m: hkt, 'a>) = 33 | let s: 'a Option = unwrap m 34 | match s with 35 | | Some m -> Just m 36 | | None -> Nothing 37 | 38 | -------------------------------------------------------------------------------- /FSTan/Monad.fs: -------------------------------------------------------------------------------- 1 | module FSTan.Monad 2 | open FSTan.HKT 3 | open FSTan.Applicative 4 | 5 | 6 | [] 7 | type monad<'M>() = 8 | inherit applicative<'M>() 9 | abstract member return'<'a> : 'a -> hkt<'M, 'a> 10 | abstract member bind<'a, 'b> : hkt<'M, 'a> -> ('a -> hkt<'M, 'b>) -> hkt<'M, 'b> 11 | abstract member combine<'a, 'b> : 12 | hkt<'M, 'a> -> hkt<'M, 'b> -> hkt<'M, 'b> 13 | 14 | default si.combine ma mb = si.bind ma <| fun _ -> mb 15 | default si.return' a = si.pure' a 16 | 17 | abstract member fail<'a> : string -> hkt<'M, 'a> 18 | default __.fail s = failwith s 19 | 20 | default si.fmap<'a, 'b> (f : 'a -> 'b) (m : hkt<'M, 'a>) : hkt<'M, 'b> = 21 | si.bind m (f >> si.return') 22 | 23 | let return'<'a, 'M when 'M :> monad<'M>> = getsig<'M>.return'<'a> 24 | let bind<'a, 'b, 'M when 'M :> monad<'M>> = getsig<'M>.bind<'a, 'b> 25 | let (>>=) a b = bind a b 26 | let combine<'a, 'b, 'M when 'M :> monad<'M>> = getsig<'M>.combine<'a, 'b> 27 | let (>>) a b = combine a b 28 | 29 | 30 | type DoNotation() = 31 | member __.Bind(m, k) = bind m k 32 | member __.Return a = return' a 33 | member __.ReturnFrom a = a 34 | member __.Combine ma mb = combine ma mb 35 | // TODO: forM 36 | 37 | let Do = DoNotation() -------------------------------------------------------------------------------- /FSTan/Monoid.fs: -------------------------------------------------------------------------------- 1 | module FSTan.Monoid 2 | open FSTan.HKT 3 | 4 | [] 5 | type semigroup<'s>() = 6 | 7 | abstract member op<'a> : 8 | hkt<'s, 'a> -> hkt<'s, 'a> -> hkt<'s, 'a> 9 | abstract member sconcat<'a> : 10 | hkt<'s, 'a> -> hkt<'s, 'a> 11 | abstract member stimes<'a> : 12 | int -> hkt<'s, 'a> -> hkt<'s, 'a> 13 | 14 | default si.stimes a b = semigroup<'s>.stimesDefault si a b 15 | 16 | static member stimesDefault si y0 x0 = 17 | if y0 <= 0 18 | then failwith "stimes: positive multiplier expected" 19 | else 20 | let rec f x y = 21 | match y with 22 | | _ when y%2 = 0 -> 23 | f (si.op x x) (y / 2) 24 | | 1 -> x 25 | | _ -> g (si.op x x) (y / 2) x 26 | and g x y z = 27 | match y with 28 | | _ when y%2 = 0 -> 29 | g (si.op x x) (y / 2) z 30 | | 1 -> si.op x z 31 | | _ -> g (si.op x x) (y / 2) (si.op x z) 32 | in f x0 y0 33 | 34 | 35 | [] 36 | type monoid<'s>() = 37 | inherit semigroup<'s>() 38 | abstract member mempty<'m> : unit -> hkt<'s, 'm> 39 | abstract member mappend<'m> : hkt<'s, 'm> -> hkt<'s, 'm> -> hkt<'s, 'm> 40 | abstract member mconcat<'m> : hkt<'s, 'm> list -> hkt<'s, 'm> 41 | 42 | default si.mappend a b = 43 | si.op a b 44 | 45 | default si.mconcat xs = 46 | let empty = si.mempty() 47 | List.foldBack si.mappend xs empty -------------------------------------------------------------------------------- /FSTan.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.168 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSTan", "FSTan\FSTan.fsproj", "{79E22A01-CABB-4731-BE2C-652F6012304C}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tutorials", "Tutorials\Tutorials.fsproj", "{1A608D0F-5165-409B-B366-51302F216FD2}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {79E22A01-CABB-4731-BE2C-652F6012304C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {79E22A01-CABB-4731-BE2C-652F6012304C}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {79E22A01-CABB-4731-BE2C-652F6012304C}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {79E22A01-CABB-4731-BE2C-652F6012304C}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {1A608D0F-5165-409B-B366-51302F216FD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {1A608D0F-5165-409B-B366-51302F216FD2}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {1A608D0F-5165-409B-B366-51302F216FD2}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {1A608D0F-5165-409B-B366-51302F216FD2}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {65DDB457-5650-41DE-AB9D-3FB0EB1C0985} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /FSTan/HKT.fs: -------------------------------------------------------------------------------- 1 | module FSTan.HKT 2 | type hkt<'K, 'T> = interface end 3 | 4 | open System 5 | open System.Reflection 6 | 7 | let private ts = Array.zeroCreate 0 8 | 9 | [] 10 | let getsig<'a> = 11 | // There is another way to do so: add constraint 12 | // `'a when 'a: (new: unit -> 'a)`. 13 | // However, if this way is adopted, users have to 14 | // manually mark each generic typevar of a type constructor, 15 | // which could be verbose and annoying. 16 | // With above considerations, I use reflection instead. 17 | let t = typeof<'a> 18 | let f = t.GetConstructor( 19 | BindingFlags.Instance ||| BindingFlags.Public, 20 | null, 21 | CallingConventions.HasThis, 22 | ts, 23 | null) 24 | let o = f.Invoke([||]) 25 | o :?> 'a 26 | 27 | 28 | // Some builtin data types like Map, List, Option cannot be interfaced 29 | // with `hkt`, so we have to wrap them. 30 | // Following methods provide a common interface to access `wrap` and `unwrap` 31 | // operations for all wrapped types. 32 | // Also, following methods implement a core infrastructure introduced from this paper: 33 | // https://www.cl.cam.ac.uk/~jdy22/papers/lightweight-higher-kinded-polymorphism.pdf 34 | // `wrap` here is a polymorphic `inj` in that paper and `unwrap` is a polymorphic `prj`. 35 | 36 | let inline wrap<'o, ^f, 'a when ^f : (static member wrap : 'o -> hkt<'f, 'a>)> (o: 'o) : hkt< ^f, 'a> = 37 | (^f : (static member wrap : 'o -> hkt<'f, 'a>) o) 38 | 39 | let inline unwrap<'o, ^f, 'a when ^f : (static member unwrap : hkt<'f, 'a> -> 'o)> (f : hkt< ^f, 'a>) : 'o = 40 | (^f : (static member unwrap : hkt<'f, 'a> -> 'o) f) -------------------------------------------------------------------------------- /FSTan/Applicative.fs: -------------------------------------------------------------------------------- 1 | module FSTan.Applicative 2 | open FSTan.HKT 3 | open FSTan.Functor 4 | 5 | [] 6 | type applicative<'F>() = 7 | inherit functor<'F>() 8 | 9 | abstract member pure'<'a> : 'a -> hkt<'F, 'a> 10 | 11 | abstract member ap<'a, 'b> : hkt<'F, ('a -> 'b)> -> hkt<'F, 'a> -> hkt<'F, 'b> 12 | default si.ap f a = si.liftA2 id f a 13 | 14 | abstract member liftA2<'a, 'b, 'c> : 15 | ('a -> 'b -> 'c) -> hkt<'F, 'a> -> hkt<'F, 'b> -> hkt<'F, 'c> 16 | 17 | default si.liftA2<'a, 'b, 'c> (f: 'a -> 'b -> 'c) (x: hkt<'F, 'a>) (y: hkt<'F, 'b>) = 18 | si.ap (si.fmap f x) y 19 | 20 | abstract member ``*>``<'a, 'b> : hkt<'F, 'a> -> hkt<'F, 'b> -> hkt<'F, 'b> 21 | default si.``*>``<'a, 'b> (a1: hkt<'F, 'a>) (a2: hkt<'F, 'b>): hkt<'F, 'b> = 22 | si.ap (si.``<$`` id a1) a2 23 | 24 | abstract member ``<*`` : hkt<'F, 'a> -> hkt<'F, 'b> -> hkt<'F, 'a> 25 | default si.``<*`` a b = si.liftA2 (fun x _ -> x) a b 26 | 27 | 28 | let pure'<'a, 'F when 'F :> applicative<'F>> : 29 | 'a -> hkt<'F, 'a> = 30 | getsig<'F>.pure'<'a> 31 | 32 | let ap<'a, 'b, 'F when 'F :> applicative<'F>> : 33 | hkt<'F, ('a -> 'b)> -> hkt<'F, 'a> -> hkt<'F, 'b> = 34 | getsig<'F>.ap<'a, 'b> 35 | 36 | let (<*>) a b = ap a b 37 | 38 | let liftA<'a, 'b, 'F when 'F :> applicative<'F>> 39 | (f : 'a -> 'b) (a : hkt<'F, 'a>) : hkt<'F, 'b> 40 | = let si = getsig<'F> in 41 | si.pure' f <*> a 42 | 43 | let liftA2<'a, 'b, 'c, 'F when 'F :> applicative<'F>> : 44 | ('a -> 'b -> 'c) -> hkt<'F, 'a> -> hkt<'F, 'b> -> hkt<'F, 'c> 45 | = getsig<'F>.liftA2 46 | 47 | let ``<*``<'a, 'b, 'F when 'F :> applicative<'F>> : 48 | hkt<'F, 'a> -> hkt<'F, 'b> -> hkt<'F, 'a> = 49 | getsig<'F>.``<*`` 50 | 51 | let (<*) a b = ``<*`` a b 52 | 53 | 54 | let ``*>``<'a, 'b, 'F when 'F :> applicative<'F>> : 55 | hkt<'F, 'a> -> hkt<'F, 'b> -> hkt<'F, 'b> = 56 | getsig<'F>.``*>`` 57 | 58 | let ( *> ) a b = ``*>`` a b 59 | 60 | // TODO, liftA3 -------------------------------------------------------------------------------- /FSTan/Control/Trans/State.fs: -------------------------------------------------------------------------------- 1 | module rec FSTan.Control.Trans.State 2 | open FSTan.HKT 3 | open FSTan.Monad 4 | open FSTan.MonadTrans 5 | 6 | type mkStateTras<'ST, 's, 'm when 'm :> monad<'m>>() = 7 | inherit monad>() 8 | default si.pure'<'a> (a: 'a): hkt, 'a> = 9 | StateT <| fun s -> return' (a, s) 10 | 11 | override si.bind<'a, 'b> 12 | (m: hkt, 'a>) 13 | (k: 'a -> hkt, 'b>): hkt, 'b> = 14 | StateT <| fun s -> 15 | runStateT m s >>= fun (a, s') -> 16 | runStateT (k a) s' 17 | 18 | interface hkt<'s, 'm> 19 | interface monadTrans, 'm> with 20 | member si.lift<'a> (m: hkt<'m, 'a>): hkt, 'a> = 21 | StateT <| fun (s: 's) -> 22 | m >>= fun a -> return' (a, s) 23 | 24 | and stateTData<'ST, 's, 'm, 'a when 'm :> monad<'m>> = 25 | | StateT' of ('s -> hkt<'m, ('a * 's)>) 26 | interface hkt, 'a> 27 | 28 | let runStateT<'ST, 's, 'm, 'a when 'm :> monad<'m>> (m: hkt, 'a>): ('s -> hkt<'m, ('a * 's)>) = 29 | let (StateT' f) = m :?> stateTData<'ST, 's, 'm, 'a> 30 | f 31 | 32 | let StateT<'ST, 's, 'm, 'a when 'm :> monad<'m>> (f: 's -> hkt<'m, 'a * 's>) : hkt, 'a> = 33 | (StateT' f) :> _ 34 | 35 | let (|StateT|) (m: hkt, 'a>) = 36 | let (StateT' f) = m :?> stateTData<'ST, 's, 'm, 'a> 37 | in StateT f 38 | 39 | let state f = StateT (return' << f) 40 | 41 | let get<'ST, 's, 'm when 'm :> monad<'m>> : hkt, 's> = 42 | state <| fun s -> (s, s) 43 | 44 | let put<'ST, 's, 'm when 'm :> monad<'m>> (s: 's) : hkt, unit> = 45 | state <| fun _ -> (), s 46 | 47 | let modify f = state <| fun s -> (), f s 48 | 49 | let gets f = state <| fun s -> f s, s 50 | 51 | let evalStateT<'ST, 's, 'm, 'a when 'm :> monad<'m>> : hkt, 'a> -> 's -> hkt<'m, 'a> = 52 | fun m s -> Do { 53 | let! (a, _) = runStateT m s 54 | return a 55 | } 56 | 57 | let execStateT<'ST, 's, 'm, 'a when 'm :> monad<'m>> : hkt, 'a> -> 's -> hkt<'m, 's> = 58 | fun m s -> Do { 59 | let! (_, s) = runStateT m s 60 | return s 61 | } -------------------------------------------------------------------------------- /FSTan/Control/State.fs: -------------------------------------------------------------------------------- 1 | module rec FSTan.Control.State 2 | 3 | open FSTan.HKT 4 | 5 | open FSTan.Monad 6 | open FSTan.Data.Identity 7 | open FSTan.Control.Trans 8 | 9 | module StateT = FSTan.Control.Trans.State 10 | 11 | type S_Id() = 12 | inherit mkIdentity() 13 | 14 | type mkState<'ST, 's> = StateT.mkStateTras<'ST, 's, mkIdentity> 15 | 16 | [] 17 | let runState<'ST, 's, 'a>(m: hkt, 'a>): ('s -> 'a * 's) = 18 | let f = StateT.runStateT m 19 | fun s -> S_Id.unwrap (f s) 20 | 21 | let State<'ST, 's, 'a> (f: 's -> ('a * 's)): hkt, 'a> = 22 | let f = fun s -> S_Id.wrap (f s) 23 | StateT.StateT f 24 | 25 | let (|State|) (s: hkt, 'a>) = 26 | let (StateT.StateT f) = s 27 | in State (fun s -> S_Id.unwrap (f s)) 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FSTan 2 | 3 | Exactly a full-featured and practical implementation typeclasses and higher kinded types in F#. 4 | 5 | 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 kinded types and constraints. 6 | 7 | 8 | 9 | ## Motivation and Features 10 | 11 | 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 to create a better one: 12 | 13 | - Support instance resolution. 14 | - Support ad-hoc polymorphism. 15 | - Support to create a typeclass and add constraints to it. 16 | - Support subtypeclassing. 17 | - Support to directly access type constructor. 18 | - Support default implementations for typeclass. 19 | - All above operations are quite lightweighted and not implemented in a magic way. 20 | 21 | Yes, exactly, it's a what many and I have dreamed about for so long. 22 | 23 | 24 | ## Limitation 25 | 26 | 1. The performance might hurt in some scenarios, for each the datatype works with 27 | higher kinded types have to be upcasted to an unique abstract class, for instance, 28 | `maybe<'a>` has to be casted to `hkt`. 29 | 30 | 2. For some builtin datatypes cannot be interfaced with `hkt`, an extra wrapper class is 31 | required to work with higher kined types. 32 | 33 | For instance, interface type `listData<'a>` is required for the builtin `List<'a>`. 34 | 35 | You can use `wrap` and `unwrap` to transform datatypes from `List<'a>` to `hkt,'a>`, vice versa. 36 | 37 | ```FSharp 38 | module List' = List 39 | type List'<'a> = List<'a> 40 | 41 | 42 | type mkList<'L>() = 43 | inherit monad>() 44 | static member wrap<'a> (x : List'<'a>): hkt, 'a> = 45 | {wrap = x} :> _ 46 | static member unwrap<'a> (x : hkt, 'a>): List'<'a> = 47 | (x :?> _).wrap 48 | 49 | default si.bind<'a, 'b> (m: hkt, 'a>) (k: 'a -> hkt, 'b>): hkt, 'b> = 50 | wrap <| List'.collect (unwrap << k) (unwrap m) 51 | 52 | default si.pure'<'a> (a: 'a): hkt, 'a> = wrap <| [a] 53 | interface show> with 54 | member si.show (x: hkt, 'a>) = 55 | let x = unwrap x 56 | x.ToString() 57 | 58 | and listData<'L, 'a> = 59 | {wrap : List'<'a>} 60 | interface hkt, 'a> 61 | 62 | 63 | // create a concrete List type 64 | type ListSig() = 65 | // default implements following type classes: 66 | // - monad (functor and applicative are implemented simultaneously) 67 | // - show 68 | 69 | inherit mkList() 70 | type list<'a> = hkt, 'a> 71 | 72 | let test() = 73 | 74 | let listm: _ list = Do { 75 | let! x = wrap [1, 2, 3] 76 | wrap [x] 77 | } 78 | // listm : resolved to be list 79 | 80 | let f (x: int) : string = "" 81 | fmap f listm 82 | // return value is resolved to be list 83 | ``` 84 | 3. Cannot implement instance for datatypes that are not constructed by a type constructor. 85 | For instance, you cannot implement any typeclass for all primitives types like integers, floats and so on, unless you wrap them with an `Identity` type constructor. 86 | 87 | -------------------------------------------------------------------------------- /Tutorials/Program.fs: -------------------------------------------------------------------------------- 1 | // Learn more about F# at http://fsharp.org 2 | 3 | open System 4 | 5 | open FSTan.HKT 6 | 7 | // define a simple typeclass 8 | [] 9 | type show<'s>() = 10 | abstract member show<'a> : hkt<'s, 'a> -> string 11 | 12 | let show<'a, 's when 's :> show<'s>> = getsig<'s>.show<'a> 13 | 14 | type myData1<'a> = // define datatype 15 | | A | B | C 16 | interface hkt 17 | 18 | and MyTypeCons1() = 19 | // define type constructor 20 | // in F#, we don't really have this, but 21 | // we can leverage a signature type(yes, this is just a signature) 22 | // and `hkt`(check FSTan.HKT, not magic at all) 23 | // to fully simulate a type constructor. 24 | inherit show() with 25 | override si.show a = 26 | // This conversion can absolutely succeed 27 | // for there is only one datatype which 28 | // interfaces hkt 29 | let a = a :?> _ myData1 30 | 31 | sprintf "%A" a 32 | 33 | 34 | type myData2<'a> = // define datatype 35 | | I of int 36 | | S of string 37 | interface hkt 38 | and MyTypeCons2() = 39 | // define type constructor 40 | // in F#, we don't really have this, but 41 | // we can leverage a signature type(yes, this is just a signature) 42 | // and `hkt`(check FSTan.HKT, not magic at all) 43 | // to fully simulate a type constructor. 44 | inherit show() with 45 | override si.show a = 46 | let a = a :?> _ myData2 47 | match a with 48 | | I a -> sprintf "isInt %d" a 49 | | S a -> sprintf "isStr %s" a 50 | 51 | 52 | let test() = 53 | let s1 = show <| I 32 54 | let s2 = show <| S "123" 55 | let s3 = show A 56 | let s4 = show B 57 | printfn "%s\n%s\n%s\n%s" s1 s2 s3 s4 58 | 59 | open FSTan.Monad 60 | open FSTan.Data.Maybe 61 | open FSTan.Data.List 62 | open FSTan.Control.State 63 | open FSTan.Data.Either 64 | open FSTan.Control.Trans.State 65 | open FSTan.MonadTrans 66 | 67 | 68 | type ListSig() = 69 | inherit mkList() 70 | 71 | type List = mkList 72 | 73 | type EitherSig<'e>() = 74 | inherit mkEither, 'e>() 75 | type Either<'e> = mkEither, 'e> 76 | 77 | 78 | type StateSig<'s>() = 79 | inherit mkState, 's>() 80 | type State<'s> = mkState, 's> 81 | 82 | 83 | type MaybeSig() = 84 | inherit mkMaybe() 85 | 86 | type Maybe = mkMaybe 87 | 88 | 89 | type either<'e, 'a> = hkt, 'a> 90 | type maybe<'a> = hkt 91 | type state<'s, 'a> = hkt, 'a> 92 | type list<'a> = hkt 93 | 94 | type S() = 95 | member __.s (x: int) = x + 1 96 | 97 | let inline app< ^F, 'a, 'c when ^F: (static member cons: 'a -> 'c)> : ^F -> 'a -> 'c = fun F a -> 98 | (^F: (static member cons: 'a -> 'c) a) 99 | 100 | open FSTan.Monad 101 | open FSTan.Control.Trans.State 102 | 103 | 104 | 105 | 106 | type StateTransSig<'s, 'm when 'm :> monad<'m>>() = 107 | inherit mkStateTras, 's, 'm>() 108 | 109 | type stateT<'s, 'm, 'a when 'm :> monad<'m>> = hkt, 's, 'm>, 'a> 110 | 111 | let plusOne<'m when 'm :> monad<'m>> : stateT = Do { 112 | let! state = get // similar to `state <- get` in haskell 113 | do! put <| state + 1 114 | return () 115 | } 116 | 117 | let testMaybe(): stateT = plusOne 118 | let testEither<'a> : stateT, unit> = plusOne 119 | 120 | [] 121 | let main argv = 122 | test() 123 | 124 | let m1 = 125 | Do { 126 | let! x = Just 1 127 | return "" 128 | } 129 | 130 | let m2 : _ list = 131 | Do { 132 | let! x = wrap [1; 2; 3] 133 | return x * 3 134 | } 135 | 136 | //let m3: state = 137 | // Do { 138 | // let! s = get 139 | // return s + 1 140 | // } 141 | //let a, b = runState m3 1 142 | 143 | let f = Right 1 144 | let m4 : either -> either = fun m -> 145 | Do { 146 | let! a = m 147 | return a + 1 148 | } 149 | 150 | let (Right 2) = m4 f 151 | 152 | let m4: stateT = Do { 153 | let! a = lift <| Just 1 154 | let! s = get 155 | return a 156 | } 157 | 158 | let s = runStateT plusOne 1 159 | 160 | 161 | 0 // return an integer exit code 162 | -------------------------------------------------------------------------------- /Guide.md: -------------------------------------------------------------------------------- 1 | # FSTan Guide 2 | 3 | 4 | Typeclasses 5 | ============= 6 | 7 | 8 | Typeclassess are achived through abstract classes, which makes it works perfect for both subtypeclassing and default implementations. 9 | 10 | If some type is constructed with a type constructor, you can implement `show` class for it. 11 | 12 | Let's have a look at how to write a `show` class and use it in polymorphism functions and even operators. 13 | 14 | 15 | ```FSharp 16 | 17 | open FSTan.HKT 18 | 19 | // define a simple typeclass 20 | [] 21 | type show<'s>() = 22 | abstract member show<'a> : hkt<'s, 'a> -> string 23 | 24 | // I have a typeclass, 25 | // I have 2 datatypes, 26 | // Oh! 27 | // Polymorphism! 28 | let show<'a, 's when 's :> show<'s>> = getsig<'s>.show<'a> 29 | 30 | type myData1<'a> = // define datatype 31 | | A | B | C 32 | interface hkt 33 | 34 | and MyTypeCons1() = 35 | // define type constructor 36 | // in F#, we don't really have this, but 37 | // we can leverage a signature type(yes, this is just a signature) 38 | // and `hkt`(check FSTan.HKT, not magic at all) 39 | // to fully simulate a type constructor. 40 | inherit show() with 41 | override si.show a = 42 | // This conversion can absolutely succeed 43 | // for there is only one datatype which 44 | // interfaces hkt 45 | let a = a :?> _ myData1 46 | 47 | sprintf "%A" a 48 | 49 | 50 | type myData2<'a> = // define datatype 51 | | I of int 52 | | S of string 53 | interface hkt 54 | and MyTypeCons2() = 55 | // define type constructor 56 | // in F#, we don't really have this, but 57 | // we can leverage a signature type(yes, this is just a signature) 58 | // and `hkt`(check FSTan.HKT, not magic at all) 59 | // to fully simulate a type constructor. 60 | inherit show() with 61 | override si.show a = 62 | let a = a :?> _ myData2 63 | match a with 64 | | I a -> sprintf "isInt %d" a 65 | | S a -> sprintf "isStr %s" a 66 | let test() = 67 | let s1 = show <| I 32 68 | let s2 = show <| S "123" 69 | let s3 = show A 70 | let s4 = show B 71 | printfn "%s\n%s\n%s\n%s" s1 s2 s3 s4 72 | 73 | ``` 74 | Output: 75 | ``` 76 | isInt 32 77 | isStr 123 78 | A 79 | B 80 | ``` 81 | 82 | 83 | Subtypeclassing 84 | ============= 85 | 86 | If you're familiar with Haskell and related stuffs, you must have an experience with this case: 87 | 88 | ```haskell 89 | 90 | class Applicative m => Monad m where 91 | -- descriptions of monad typeclass 92 | ``` 93 | 94 | Above code descibes the an example of dependencies between typeclasses, and `Monad` 95 | is exactly a subtypeclass of `Applicative`. 96 | 97 | After introducing this sort of constraints, a higher level abstraction is achieved, which enables reaching a higher ratio of code reuse. 98 | 99 | For instance, a `Monad` instance requires implementations of many specific methods(`return`, `bind`, `combine` and so on), but we already have a knowledge about subtypeclassing information of `Monad`, it's a subtypeclass of `Functor` and `Applicative`, so if we implement `pure` from `Applicative` and implement `bind` from `Monad` for an instance(eg. `Maybe`) of `Monad`, we then implement all instances of `Functor`, `Applicative` and `Monad`. 100 | 101 | In F#, we can implement `Either` `Monad`(also `Functor` and `Applicative`). 102 | 103 | ```FSharp 104 | open FSTan.HKT 105 | open FSTan.Show 106 | open FSTan.Monad 107 | 108 | type either<'e, 'a> = hkt, 'a> 109 | 110 | and EitherMonad<'e>() = 111 | inherit monad>() with 112 | override __.pure'<'a> (a: 'a): either<'e, 'a> = Right a :> _ 113 | override __.bind<'a, 'b> (m: either<'e, 'a>) (k: 'a -> either<'e, 'b>): either<'e, 'b> = 114 | match m :?> eitherData<'e, 'a> with 115 | | Left l -> Left l :> _ 116 | | Right r -> k r 117 | interface show> with 118 | member __.show<'a> (a: either<'e, 'a>) = 119 | let a = a :?> eitherData<_, _> 120 | a.ToString() 121 | 122 | and eitherData<'e, 'a> = 123 | | Left of 'e 124 | | Right of 'a 125 | interface either<'e, 'a> 126 | 127 | let Left<'e, 'a> (e: 'e) : either<'e, 'a> = Left e :> _ 128 | let Right<'e, 'a> (a: 'a) : either<'e, 'a> = Right a :> _ 129 | let (|Left|Right|) (m: either<'e, 'a>) = 130 | match m :?> eitherData<'e, 'a> with 131 | | Left l -> Left l 132 | | Right r -> Right r 133 | ``` 134 | 135 | Abobe codes in Haskell is similar to 136 | 137 | ```Haskell 138 | 139 | data Either e a = Left e | Right a 140 | 141 | instance Functor (Either e) where 142 | -- ... `fmap` and other "methods" are automatically implemented by `bind` and `return`. 143 | 144 | instance Applicative (Either e) where 145 | pure = Right 146 | -- ... 147 | 148 | instance Monad (Either e) where 149 | return = pure 150 | bind m k = \case 151 | Left l -> Left l 152 | Right r -> k r 153 | 154 | instance Show (Either e a) where 155 | -- this section is not that similar to haskell 156 | ``` 157 | 158 | Feel free to check [implementation of `Monad` in FSTan](https://github.com/thautwarm/FSTan/blob/master/FSTan/Monad.fs). 159 | 160 | 161 | 162 | Higher kined types 163 | ================== 164 | 165 | ```FSharp 166 | let test_hkt<'a, 'b, 'c> (f: hkt<'a, 'b>) : hkt<'b, 'c> = 167 | /// impl 168 | ``` 169 | 170 | In terms of above snippet, if `c` is a concrete type, then `'a` has kind `* -> * -> *`, as well as `b` has kind `* -> *`. 171 | 172 | 173 | Do notation 174 | ===================== 175 | 176 | Open `FSTan.Monad`, where a computation expression is provided to be an alternative of `do` in Haskell. 177 | 178 | ```FSharp 179 | 180 | open FSTan.Monad 181 | open FSTan.Control.Trans.State 182 | open FSTan.Data.Maybe 183 | open FSTan.Data.Either 184 | 185 | let plusOne<'m when 'm :> monad<'m>> : stateT = Do { 186 | let! state = get // similar to `state <- get` in haskell 187 | do! put <| state + 1 188 | return () 189 | } 190 | 191 | let valueMaybe : stateT = plusOne 192 | // type maybe<'a> = hkt 193 | 194 | let valueEither<'e> : stateT, unit> = plusOne 195 | // type either<'e, 'a> = hkt, 'a> 196 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------