Skip to content

Commit

Permalink
fix runtime and add guide
Browse files Browse the repository at this point in the history
  • Loading branch information
thautwarm committed Dec 26, 2018
1 parent 8332e47 commit 3390b7e
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 7 deletions.
8 changes: 7 additions & 1 deletion FSTan.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions FSTan/Data/List.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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>}
Expand Down
5 changes: 3 additions & 2 deletions FSTan/Data/Maybe.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ open FSTan.Monad
type maybe<'a> = hkt<Maybe, 'a>
and Maybe() =
inherit monad<Maybe>() with
static member si = Maybe()
override __.bind<'a, 'b> (m: maybe<'a>) (k: 'a -> maybe<'b>) =
let m = unwrap m
match m with
| Some a -> k a
| 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>}
Expand Down
2 changes: 1 addition & 1 deletion FSTan/Functor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
open FSTan.HKT

[<AbstractClass>]
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>
Expand Down
14 changes: 13 additions & 1 deletion FSTan/HKT.fs
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
module FSTan.HKT
type hkt<'K, 'T> = interface end

open System
open System.Reflection

let private ts = Array.zeroCreate<Type> 0

// seems to be silly...
// any default object?
[<GeneralizableValue>]
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.
Expand Down
1 change: 1 addition & 0 deletions FSTan/Monoid.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ open FSTan.HKT

[<AbstractClass>]
type semigroup<'s>() =

abstract member op<'a> :
hkt<'s, 'a> -> hkt<'s, 'a> -> hkt<'s, 'a>
abstract member sconcat<'a> :
Expand Down
87 changes: 87 additions & 0 deletions Guide.md
Original file line number Diff line number Diff line change
@@ -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
[<AbstractClass>]
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<MyTypeCons1, 'a>
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<MyTypeCons1>() with
override si.show a =
// This conversion can absolutely succeed
// for there is only one datatype which
// interfaces hkt<MyTypeCons1, 'a>
let a = a :?> _ myData1
sprintf "%A" a
type myData2<'a> = // define datatype
| I of int
| S of string
interface hkt<MyTypeCons2, 'a>
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<MyTypeCons2>() 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 `* -> *`.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
62 changes: 62 additions & 0 deletions Tutorials/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Learn more about F# at http://fsharp.org

open System

open FSTan.HKT

// define a simple typeclass
[<AbstractClass>]
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<MyTypeCons1, 'a>

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<MyTypeCons1>() with
override si.show a =
// This conversion can absolutely succeed
// for there is only one datatype which
// interfaces hkt<MyTypeCons1, 'a>
let a = a :?> _ myData1

sprintf "%A" a


type myData2<'a> = // define datatype
| I of int
| S of string
interface hkt<MyTypeCons2, 'a>
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<MyTypeCons2>() 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

[<EntryPoint>]
let main argv =
test()
0 // return an integer exit code
16 changes: 16 additions & 0 deletions Tutorials/Tutorials.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FSTan\FSTan.fsproj" />
</ItemGroup>

</Project>

0 comments on commit 3390b7e

Please sign in to comment.