CARVIEW |
Navigation Menu
-
-
Notifications
You must be signed in to change notification settings - Fork 437
Language-Ext 2024 Roadmap #1289
Replies: 7 comments · 8 replies
-
I am really excited to hear, that you are investing more time and moving forward with If I understood correctly, then |
Beta Was this translation helpful? Give feedback.
All reactions
-
Not quite, the way to think about this is it's a little bit like the Roslyn API, but it works only for types. But because values are also types it is effectively a whole language API. That's because generic-types that have values applied as their generic arguments are no different to lambda-functions. It's all just a case of when it runs. The when is important. Because that's where the flexibility comes in. You could:
For example, you can use the raw API to define an option-type: var optionA =
TLam("a",
Sum(Case("Some", TVar("a")),
Case("None", Empty))); This creates a 'type lambda abstraction' (this is the generic parameter), with a Now you have an in-code specification for a generic sum-type. Then you can apply an argument to it: var optionInt = TApp(optionA, Ty.Integer); Now you have an Or, you could instead apply a value: var option123 = TApp(optionA, Ty.Value(123)); Now you have an You can then check if one is the sub-type of the other: option123.IsSubTypeOf(optionInt) Which means you can create data 'types' that represent the raw data in your application (think: a live web-request, or whatever). You can then check if that raw, live, data-type is a sub-type of the wider expected type, which validates the input in the same way a compiler validates your code. In reality you'd want to apply the var some123 = Cons(optionInt, "Some", Ty.Value(123)); Then you can do the sub-typing check again: some123.IsSubTypeOf(optionInt) This will validate that Because the type-system is super flexible, it has other types that you just don't get in C#, like true union-types: var option1or2or3 = TApp(optionA, Exists(Ty.Value(1), Ty.Value(2), Ty.Value(3))); Now some123.IsSubTypeOf(option1or2or3) Because The sub-typing aspect of this (not OOP subtyping, but partial orders) is fundamental. It means we can treat data as types and we can describe types that the data should be 'less-than or equal to'. If a type is less-than or equal to another type then it's the subtype of it and can therefore be assigned to it (i.e. it type checks). Now obviously if we're declaring types, functions, etc. then we should have a type-system to put them in; to build more complex types (like records, etc.) that refer to each other. Here's a small example from one of my prototypes: Universe.Ours.Process(
TypeSystem("Global",
Declare("Option",
TLam("a",
Sum(Case("Some", TVar("a")),
Case("None", Empty)))),
Declare("Either",
TLam("l",
TLam("r",
Sum(Case("Left", TVar("l")),
Case("Right", TVar("r")))))),
Declare("Seq", TLam("a", List(TVar("a"))))),
TypeSystem("Reader",
Declare("Either", DotNet(typeof(EitherReader<,>))),
Declare("Option", DotNet(typeof(OptionReader<>))),
Declare("Seq", DotNet(typeof(SeqReader<>)))
),
TypeSystem("Writer",
Declare("Either", DotNet(typeof(EitherWriter<,>))),
Declare("Option", DotNet(typeof(OptionWriter<>))),
Declare("Seq", DotNet(typeof(SeqWriter<>))))); This uses the 'top level' universe, called
The Obviously, having to make the reader and writer implementations per-type is awkward, but that's where the source-generators will come in. The ability to generate the reader and writer implementations automatically and add them to the You would only need to implement the reader/writers manually when you have a type that doesn't 'play by the rules'. An example is public class OptionReader<A> : TyReader
{
public static Context<Option<A>> Read(Ty obj) =>
obj is TyCons cons
? cons.Constructor switch
{
"Some" when
cons.Items.Count == 1 &&
cons.Items[0] is TyValue item =>
Context.Pure(Option<A>.Some(item.CastUnsafe<A>())),
"None" when
cons.Items.Length == 0
=> Context.Pure(Option<A>.None),
_ => Context.Fail<Option<A>>(
ErrorsExt.SerialisationFailed($"Unknown constructor for Option: {cons.Constructor} or invalid number of arguments"))
}
: Context.Fail<Option<A>>(ErrorsExt.SerialisationFailed($"Unknown type for Option: {obj}"));
public Context<Ty> ReadValue(Ty data) =>
Read(data).Map(Ty.Value);
}
public class OptionWriter<A> : TyWriter
{
static readonly Ty Type =
Ty.TApp(Ty.Id("Option"), Ty.DotNet<A>());
Context<Ty> Write(Option<A> ma, Func<object?, Context<Ty>> valueConstruct) =>
ma.Match(
Some: r => valueConstruct(r).Map(x => Ty.Cons(Type, "Some", x)),
None: () => Context.Pure(Ty.Cons(Type, "None")));
public Context<Ty> WriteValue(Ty value, Func<object?, Context<Ty>> valueConstruct) =>
value is TyValue<object> { Annotation: Option<A> val }
? Write(val, valueConstruct)
: Context.Fail<Ty>(ErrorsExt.SerialisationFailed($"Unknown type for Option: {value}"));
} Again, this is all from my prototype, so it might look better or worse in the final version! But for generated code, where we know everything about the type (like with Now, the real beauty of this is that one you're in the Meta type-system rather than the C# type-system, you're not limited to C#'s types and can therefore build more expressive types, do more complex projections, and generally go crazy. But once you're done you can then project back to a C# afterwards. Imagine:
This is a very simple example that shows a validation pipeline that validates before we ever get a C# concrete type. But there could be myriad intermediate stages, which leveraged: universals, existentials, unions, records, multiple type-systems, even multiple universes, etc. For new source-generators, being able to convert .NET types into Meta types and then performing transformations on them using proper type-algebra will make it soooo much more powerful. Finally, there is a scripting system. This is not necessarily expected to turn into some major language or anything - although I guess it could if it got some momentum. Really, it's to make the authoring of types slightly easier than using the API (for where that makes sense). Here are some examples from my prototype (which actually all works, type-checks, and runs as expected): // Either discriminated union type
Either l r = union {
Left l,
Right r
};
// Option discriminated union type
Option a = union {
Some a,
None
};
// Example of a union type (not discriminated)
OtherOpt a = Some a | None | Integer;
// Creates a concrete OtherOpt type
OtherOptInt = OtherOpt Integer;
// Creates a concrete Option type
OptionInt = Option Integer;
// Is Option 1 a subtype of Option Integer? (True)
OptSub1 = Option 1 <: OptionInt;
// Is OtherOpt a subtype of Option a? (False)
OptSub2 = OtherOpt <: Option;
// Is Option a subtype of OtherOpt? (True)
OptSub3 = Option <: OtherOpt;
// Example of a higher-kinded type
Monad (m : * -> *) = {
bind a b = m a -> (a -> m b) -> m b,
pure a = m a
};
// Example of a higher-kinded type
Functor (f : * -> *) = {
map a b = (a -> b) -> f a -> f b
};
// Create a concrete Option monad
MOption = Monad Option;
// Create a concrete Option functor
FOption = Functor Option;
// Create an Either monad
MEither l = Monad (Either l);
// Create an Either functor
FEither l = Functor (Either l);
// Create an Option 100
Foo = Some 100;
// Create an Option Integer
Bar = Some Integer;
// Is Foo a subtype of Bar (True)
Test1 = Foo <: Bar;
// Is Bar a subtype of Foo (False)
Test2 = Bar <: Foo;
// Higher-kinded sub-type checker
Sub (m : * -> *) x y =
m x <: m y;
// Is Option 100 a subtype of Option Integer (True)
Test3 = Sub Option 100 Integer;
// Is Option Integer a subtype of Option 100 (True)
Test4 = Sub Option Integer 100;
// Linked-list type
List a = union {
Cons a (List a),
Nil
};
// Union-type that can only be one of three values
Val = 1 | 2 | 3;
// Example of constructing an empty list
Items1 = Nil;
// Example of constructing a list
Items = Cons (1 : Val) (Cons (2 : Val) (Cons (3 : Val) Nil)); Much is missing from these examples, but already you can see that higher-kinds, records, unions, discriminated unions, values-as-types, and sub-typing are all there. |
Beta Was this translation helpful? Give feedback.
All reactions
-
β€οΈ 1
-
You could probably also build some of your types DSL nicely with Spitballing on your first example, without time at the moment to build a working prototype, but you could get things out of: Expression<Func<AType, SomeMetaType>> Some = a => a;
SomeMetaType None = new();
Expression<Func<AType, SomeMetaType>> OptionA = a => Some(a) | None;
// Something like this might even be possible with the right function definition
SomeMetaType Meta<T>(Expression<Func<in T, SomeMetaType>> expression) => /* β¦ */;
var Some = Meta(a => a);
var None = Meta();
var OptionA = Meta(a => Some(a) | None); |
Beta Was this translation helpful? Give feedback.
All reactions
-
β€οΈ 1
-
Glad to hear some news, as I thought something bad happend recently that caused radio-silence. I've accumulated several questions during this time that I would like to ask you:
What do you think about F*, proof-oriented systems and dependent types in general? Do these have any chances to become more popular in the future, or it way too academic stuff just for researching?
|
Beta Was this translation helpful? Give feedback.
All reactions
-
All great questions, however I'm away from my machine for the weekend and typing a full reply on my phone would take ages! So I'll get back to you with a full reply next week. But, in terms of not being that visible recently: that's mostly because I've been focusing on the v5-transducers branch (there's a huge amount of activity on there), and the Meta prototype which is private at the moment (will open it up soon). Prototyping is just how I write code. I brute force every difficult coding problem by building, sometimes many, prototypes. Some might say that's inefficient and would instead advocate more upfront design, but most who know me know I have extraordinarily high output (quality and quantity). In the case of the current roadmap, this is a junction of many big ideas that have been percolating in my mind for a long time. My comments about reducing complexity is entirely where my brain is going. And so when I prototype I am trying to make real these ideas in a way that has the lowest cognitive load (which is another reason why prototypes are good, you get too feel the complexity for real). I really want to create a toolkit that everyone can use to build complex functional compositions. A bit like Haskell, but even more. Transducers can be thought of as the LEGO bricks of monads, functors, etc. Meta can be thought of as 'the truth' of your distributed application. Marrying these ideas and making them easy to use is complex, hence why it's taking me a while to release some of this stuff. My biggest worry is having to undo something, or fundamentally change an implementation (of say the Transducer type) not long after releasing it (with the breaking changes it entails). So this one needs to be right. It also needs to be right for my new startup, which builds on these ideas and takes them into the stratosphere. So really it will be a case of releasing once I feel comfortable everything is in a good place. |
Beta Was this translation helpful? Give feedback.
All reactions
-
β€οΈ 1
-
A follow up to your specific questions:
Other function based monads ( Data-based monads (like Sequence based monads (
Transducers themselves have a small, fixed, runtime: In terms of the other types, they will only have runtimes if that type needs a runtime, it's not a requirement of the Transducer system. And so, from that point, there's not much difference to how language-ext is now.
However, one of the future benefits of Not that this couldn't be done with a regular source-generator that leverages Roslyn, but should be much easier with
Green threads won't happen in .NET. Even if they did, it would just be something I could use internally with the Transducers (which is where my 'green thread' code is). It may reduce some overhead but unlikely to make much difference. In terms of anything else they might do. That's just a bit of a "yeah, they might do anything" question - it's obviously impossible to know completely. My impression is that, from the language point-of-view, we're unlikely to ever see higher-kinds, we definitely aren't going to see dependent-types, and we almost certainly won't see refinement-types. These are all things I'm bringing into Some things I hope we see:
My old-skool games-programmer brain really wants to find ways to reduce allocations. The above two items would really help.
I mentioned in my previous answer why I prototype so much. In terms of restrictions in C#, well, a library like this is very much at the edge of C#'s capabilities, so I bump into a number of things that it can't do that I want it to do. It's mostly limitations of the type-system, but it can also be the fact that LINQ and lambdas allocate all the time - and because this is a library I want to use (and I want others to use) I have to take things like memory-allocations very seriously and try to find ways of mitigating them as much as possible. On the whole though C# is excellent and the CLR is excellent - so this really is just trying to build on some exceptional foundations.
Its maths. And maths works. And so having more rigourous and expressive type-systems will always improve the quality of the code that you write. It does come with more upfront effort by the developer and that can sometimes be a bit frustrating; but if your motivation is to write complex software that is stable and easy to manage then (I believe) it's the best way. But, that isn't even the biggest issue: trying to find good, well supported libraries, for common things needed in business domains - like RDMBS support, or good cloud access libraries, etc. is hard. And if they don't exist you have to build them yourself, slowing everything down. I even noticed this when working on a Haskell project for a year - it felt like we spent 50% of our time trying to find suitable libraries to support the scope of the project (which granted was a complex project, but they all existed for C#, Java, etc.) For any of these ecosystems to become popular it needs some good momentum and some dedicated individuals to build what already exists elsewhere, it's tough. They also need to 'talk' to the 'professional engineer'. If I see one more Fibonacci example I think I'll scream! Haha! Real world examples for real world engineers. That's why I think having something that can give you a lot of the benefits of a more 'academic' language, whilst still having access to the entire C# ecosystem could pay dividends. This is especially true at the 'edges' of your application, to stop bad data getting in, or leaving. But really, it's true for all code.
Dependent-types are just types that depend on values, so from that point-of-view it's the same. I will also be supporting refinement-types which I think really is the killer feature. We're not going to be using
I knew I wanted something that could represent any C# value - and so Therefore, I don't define nat = Zero | Succ nat Which is one difference. But my system does support values as types. So, for example: Foo = 1 | 2 | "fred";
IsFred (x : Foo) = {
match(x)
{
1 => False,
2 => False,
"fred" => True
};
}
Bar1 = IsFred 1;
Bar2 = IsFred "fred";
If that all still seems confusing, here's what it might look like in hand-written C#: record N1;
record N2;
record Fred;
abstract record Foo;
record Foo_Case1(N1 Value) : Foo;
record Foo_Case2(N2 Value) : Foo;
record Foo_Case3(Fred Value) : Foo;
record IsFred<X> where X : Foo
{
public static readonly bool Result =
typeof(X) == typeof(Foo_Case1) ? false
: typeof(X) == typeof(Foo_Case2) ? false
: typeof(X) == typeof(Foo_Case3) ? true
: throw new NotSupportedException();
}
record Bar1 : IsFred<Foo_Case1>; // Bar.Result == false
record Bar2 : IsFred<Foo_Case3>; // Bar.Result == true One thing I've noticed in dependently-types languages is that they still have a distinction between values used in types and what we would classically think of types. I've not done that. Everything is a type, full stop. I don't know, yet, whether that will come back to bite me. Another difference is that I have the concept of multiple type-systems, which can support morphisms between them. I think that's pretty unique to any language, but maybe there's something out there.
Remember, no-one pays unless their turnover is at least $1,000,000 (proposed). If there's no way an organisation like that can transfer funds then they won't be able to use the dual-licensed features unfortunately. Being a 1 person operation doesn't really give me the bandwidth to look for solutions to that problem. |
Beta Was this translation helpful? Give feedback.
All reactions
-
β€οΈ 4
-
As always, thanks for the detailed response, I think I get your thoughts and mood about all of this.
Couldn't agree more :D Just a few simple questions I forgot to ask:
|
Beta Was this translation helpful? Give feedback.
All reactions
-
They won't ever replace the wiki or any other documentation. Documentation shouldn't be pay-walled. But more in-depth presentations of language-ext (that take significant portions of my time) will need to be paid for. A. because I need a motivation to do it, and B. because there are other things I could do with that time! |
Beta Was this translation helpful? Give feedback.
All reactions
-
π 6
-
Hoping for a book from you for years, obvious pre-order. |
Beta Was this translation helpful? Give feedback.
All reactions
-
π 9
-
Also ready to pre-order it. Functional Programming in C# from Enrico Buonanno is great and lots of those concepts can then be applied when using Language-Ext but a proper book on Language-Ext would be a blessing. I always found your wiki articles well written and the theory well explained. Really looking forward to a book full of them :) |
Beta Was this translation helpful? Give feedback.
All reactions
-
π 3
-
Thanks for the support π I know that if I sit down to write a book it'll probably never happen. So, what I'm doing is building up articles on my blog - this has immediate value to users of the library. I am hoping that I'll get to a point where I have enough content, so that I can then adapt it into a book. This seems like an approach that's got a greater chance of succeeding and if it doesn't there's still plenty of content on the blog. |
Beta Was this translation helpful? Give feedback.
All reactions
-
β€οΈ 6
-
The blog is a great idea, I am your latest subscriber. Interesting approach to the HKTs.
We are just starting a new project and hopefully we will be able to convert to v5 once its officially out.
From: Paul Louth ***@***.***>
Sent: Friday, May 24, 2024 3:22 PM
To: louthy/language-ext ***@***.***>
Cc: Pascal Charbonneau ***@***.***>; Comment ***@***.***>
Subject: Re: [louthy/language-ext] Language-Ext 2024 Roadmap (Discussion #1289)
Thanks for the support π
I know that if I sit down to write a book it'll probably never happen. So, what I'm doing is building up articles on my blog<https://paullouth.com/> - this has immediate value to users of the library.
I am hoping that I'll get to a point where I have enough content, so that I can then adapt it into a book.
This seems like an approach that's got a greater chance of succeeding and if it doesn't there's still plenty of content on the blog.
β
Reply to this email directly, view it on GitHub<#1289 (reply in thread)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/A7WZV4YWARFZGWKZOJMAZO3ZD6HLZAVCNFSM6AAAAABBYHLYNKVHI2DSMVQWIX3LMV43SRDJONRXK43TNFXW4Q3PNVWWK3TUHM4TKNJRGAZDI>.
You are receiving this because you commented.Message ID: ***@***.******@***.***>>
|
Beta Was this translation helpful? Give feedback.
All reactions
-
wdm by dt? may be you mean refined types? for example
To work in refined types seems. But in dt,
seems to be dt. I guess it may be hard make |
Beta Was this translation helpful? Give feedback.
All reactions
-
when you say |
Beta Was this translation helpful? Give feedback.
All reactions
-
As for getting income, having something like educative/udemy(in educative style) - I will buy. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
After nearly 19 years I have left my full-time role as CTO at Medical Management Systems (although I'm staying on as a non-exec director and technology advisor to the board) and am now investing more time into personal projects, including bootstrapping a new idea I have for my next startup.
History
Much of the reason I built language-ext was to make programming in C# more robust, less error prone, and to enable declarative coding, which leads to easier long-term maintenance of complex code-bases. Language-Ext started as a pet project that then became vital to the success of meddbase.com which grew to over 10 million+ lines-of-code, and is probably used in every
.cs
file in the project.Seeing the pain points of large, complex software, up-close allowed me the insight to see why a project like language-ext is so needed - but it also allowed me to be very opinionated on what it is and how it should be built.
There aren't many people who have managed an 18+ year long project from the first line of code to a behemoth and kept the thing on track through that period. And there's no courses or reference manuals on how to do it either!
Industry
Many applications written today start off as quite simple, but they are also on the 'this project will last forever track'; forever growing in complexity, year-on-year (I'm thinking web-applications, phone-apps, etc.). And so there are many more projects that will fall into the 'complexity well' and will need to start looking at how to manage that cognitive load before becoming unmanageable.
What we see in the industry is that people are trying to deal with the complexity problem through micro-services and lately AI co-pilots. All these 'solutions' do is create new complexity problems. Micro-services allow for relatively easy containerisation of code but swap it for deployment complexity, weak typing, integration and synchronisation issues. AI co-pilots make code easier to write, but reduce the overall understanding of the system by the human writing it, adding cognitive load eventually. Writing code is not the hard bit: maintaining it forever is.
Not that there's no place for these things and AI will be a big part of the future of software development, I'm sure, but it doesn't fix the problems: we're still writing code like we did in the 90s - for simple applications, used by 1 person, on a single core machine. The solutions put in place to deal with the distributed computing landscape, we're now in, are sticking plasters at best.
Me + language-ext = ?
Now that I have left that project you may be concerned that I won't be continuing the support of language-ext. That isn't the case and I will be using it for my new projects which will take the ideas of language-ext waaaay beyond where it is now.
However, because I am now not in a full-time role, I will be trying to make language-ext viable financially, if possible. I have a bit of a runway, so it's not essential that it pays immediately, but within a year I'd like to see some movement with it.
That will not mean switching the licence of the
Core
of language-ext and all of the existing libraries from MIT. The MIT licence will stay, but I will be looking at a Dual-licencing approach for the new functionality I plan to build around the core of language-ext.Why can't I just rely on donations? Well, language-ext is accruing about $50 per month at the moment. And so, I can't see the donation model working reliably going forward.
However, if you are reading this and you are able to sway the decision makers at your organisation to set up a regular donation then it would be very much appreciated! If the needle moves significantly before I deploy release versions of the new functionality, then I may well hold off on the more commercial licencing.
Other non-code things I'll be considering are:
Advisory and mentoring - if you use language-ext in your organisation and you think you could benefit from some of my time with your team then I will be looking into consultancy and mentoring programmes. Feel free to email me at:
paul@level83.com
Patreon based courses/tutorials
A book - on FP architecture leveraging language-ext
Dual licence
This will be defined soon, but I will be looking at something along the lines of:
Core
,CodeGen
,Parsec
,Sys
,SysX
,FSharp
,Rx
,Transformers
The code will always be open-source it just won't always be free (i.e. the source is open, but using it will require a licence for commercial use).
Roadmap 2024
0. Bring higher-kinded types and transducers to
LanguageExt.Core
- licence: MITThis is a massive refactor to bring in higher-kinded types to allow some of the more general FP programming you see in languages like Haskell. This will include new functors, applicatives, monads, and monad-transformers (for real, not with extension methods)!
You can read more about it here as I build up to the v5 release.
1.
LanguageExt.Meta
- licence: DualThis will be an advanced dependently-typed type-system and runtime. This is very much going to be the foundational architecture of much of my future work.
I have a strong belief that software-engineering becomes more robust when we use well-defined types (which is much of the reason for language-ext to exist!), but C#'s type-system isn't expressive enough to take this as far as we could with other languages like Idris (or other theorem-prover languages).
However, those other languages are really out of the reach of the average dev working in industry. Either the ecosystem isn't there or the employer is not willing to move away from a mainstream language (like C#). Even a language like F# is not particularly expressive once you get past discriminated-unions (and won't ever be, if comments by Don Syme are anything to go by).
I want to create something that absolutely helps with very real issues in developing commercial and enterprise software:
These questions matter at every scale of application, from the smallest to the largest, but they become more acute as the complexity of an application grows. Having tools to manage that from the start of an application's life will make that growth much more manageable.
Something that I feel we get with the more expressive languages (Haskell, Idris, Coq, etc.) is an 'academia tax' - whether it's the lexicon used, the attitudes of the community, even just the things that the community tends to obsess over doesn't always overlap with the real world problems etc. Not that this is bad, it just doesn't talk to the average developer working in the real world of commercial software.
I want to make this stuff accessible without disappearing into theory-land; which won't be easy, as some of this stuff comes directly out of Category Theory, but if we can all agree that more expressive types leads to more robust code then that's a good start. Finding the balance between the theory and the practice will be essential.
Features:
1
,"hello"
, etc.Unit
in language-ext. It is a type with exactly one value:unit
. So, you can think of"hello"
as a type with exactly one value:"hello"
, which is different to the type"world"
. But you could then define a type:"hello" | "world"
that is a type that accepts one of two values (likebool
but with different constants).One thing that people often don't think about when using languages like C# is how much effort you have to put in at runtime to refine your types. If you think about how you might write C# today; every time you write:
What you're actually doing is taking a poorly spec'd type and trying to refine it at runtime. Those
if
statements are part of the type itself!. You just don't realise it! Or, looking at it another way, you're creating a new anonymised sub-type.We see this with the nullable-references feature in C#, where this:
You can see that the type has been refined by an
if
statement. It's insane when you think about it, because it means the type you pass around changes shape without changing its identity. It may well be a pragmatic solution for a C# that previously allowednull
to propagate and causeNullReferenceException
time-bombs., but we can do better by building a type-system that has proper refinement constructs.This system aims to be the ultimate validation system: never allowing bad values to exist and leveraging propositions to refine the types into a their most minimal form.
What you could use this project for:
/v1
endpoint, automatically. This can facilitate partial rollouts of microservices, for example, or make backwards compatibility something that's trivially easy to support, with little overhead.LanguageExt.Meta
as the virtual-machine for your DSL allows for a richly-typed DSL that is also sandboxed - all you have to do is write the parser for the syntax (and you can useLanguageExt.Parsec
for that!).2.
LanguageExt.Serialisation
- licence: DualEach data-type in language-ext will support transformation to-and-from
LanguageExt.Meta
types. This will mean, for example, types likeEither
,Option
, etc. get represented as real union-types inMeta
(which C# can't do properly right now).A set of serialisation libraries that convert to-and-from
LanguageExt.Meta.Type
will then be developed:LanguageExt.Serialisation.Newtonsoft
LanguageExt.Serialisation.System.Json
LanguageExt.Serialisation.XmlDocument
LanguageExt.Serialisation.Protobuf
Meaning that any C# type - that provides a conversion to a
LanguageExt.Meta.Type
representation - will automatically be serialisable/deserialisable with any of the above libraries (and any future ones added).This is super beneficial as the serialisers can stay static and written - no need to update them, and serialisation of all types are consistent - meaning that any tooling that leverages the output doesn't end up dealing with weird edge-cases. Serialisation is often fraught with problems due to the untyped nature of the transformation, but this will make all of that go away.
3.
LanguageExt.SourceGen
- licence: DualThis will be the successor to
LanguageExt.CodeGen
. A perfect example of whereLanguageExt.Meta
will be useful is for source-generation. The currentCodeGen
project is quite messy and ugly and is closer to a text-templating tool than anything that fundamentally understands the types it consumes and generates. That lack of understanding of the types severely limits the scope of any code-generation.What I want to do is create a toolkit that works with the type-system in
LanguageExt.Meta
, to projectMeta
types into C# source-code. For anyone that's used C# Source Generators, you will know that it can read existing C# code and use that to generate new C# code. But a layer on top of that could read C# types intoLanguageExt.Meta
types, and then use projections (morphisms) to generate newLanguageExt.Meta
types, which can then be projected back into C#.This allows the Source Generator to do type-checking with a type-system that is much more powerful than C#'s type-system and to do transformations that would be extremely complex with the existing simple C# source transformers.
As well as existing C# types being imported into the
LanguageExt.Meta
type-system, we will also allow, non-C#, scripted types that can support more expressive-types that leverage higher-kinds, ad-hoc polymorphism, but also dependent-types to be imported and then projected.These core tools can then be used to easily build:
But, the big point is not so much what projects I can provide, but that the tooling will exist for you to generate complex types without having to deal with Roslyn and the crazy API for C# source-generation: Meta-programming on steroids.
Another major benefit is that because
LanguageExt.Meta
is essentially leveraging lambda-calculus, we can also optimise the generated code, using Beta-reduction. Beta reduction is the process of invoking the (type) expressions to reduce to their most minimal (and therefore most efficient) form.Once in [the more expressive form of the
LanguageExt.Meta
type-system] more complex code-generation can happen because of the ability to infer and transform more reliably using well-known techniques like theorems for free. This just isn't possible with the current approach to code-gen (well, not easily, and is likely to be error prone).Other features of
LanguageExt.SourceGen
will likely be:LanguageExt.Meta
types, and it generates an API surface for you that properly validates data in and out of your system. The types can then be used in any audit documentation to prove correctness.Also, all source-gen'd types will automatically support transformation between the C# type and its
LanguageExt.Meta
type equivalent. Which means any additional functionality built to support theLanguageExt.Meta
type-system will automatically provide extra capabilities to the generated-types. The obvious first example is serialisation, but the scope is absolutely enormous!Conclusion
Because I will be using these new capabilities to build the bigger idea that I hope to be my new startup, I am committed to all of them - but just need to keep an eye on my cash in the meantime. Being full-time on language-ext and some of the bigger ideas for improving software engineering will pay dividends for everyone who uses language-ext. I wish I could talk about the real big picture project right now, but I'm going to keep that under wraps for the moment - as I still have a lot of typing to do!
So, please donate if you can afford it. If you use it at work, please ask them to donate also.
And, if you have any thoughts, ideas, or questions about this roadmap or any of the features, please let me know.
Beta Was this translation helpful? Give feedback.
All reactions