Replies: 33 comments
-
Thanks for your detailed reply. You didn't mention Compile-Time Code Execution (CTCE). Given the name "D#", is that in your plans? The main issue that sticks out in my mind is compatibility with C#. There are many programming languages in the world and people are making new ones all the time. I don't really want to make a programming language "for fun" - I want to make a meaningful contribution to the software industry. In fact, I'm starting to think about how to build an interoperability framework for WebAssembly, as soon as the first beta of the latter becomes available, because I think WebAssembly is the "future of the computer industry" (or at least, it will be if I have any say in the matter, but I'm just one guy with very little money, so... we'll see.) I see new general-purpose language projects popping up all the time, and I don't like it. In my opinion there's no point in just making a language that is "better than C++" or "better than Java" or "better than C++", since several languages fitting these descriptions already exist, and many of them are even stable. Enhanced C# is really a gimmick - it's not the best language I could create, and it's not intended to be. I'm developing it because I think that backward compatibility is a potential fruitful way to put more power in the hands of developers. Switching languages is a risky business maneuver, and a potentially difficult one if you already have a C# code base. I think the cost and perceived risk of switching largely explains why there is slow uptake of nice languages like D, Rust, Nemerle and even Ceylon. Languages perceived to be obscure, like Nim, Plaid, or Dyvil, don't stand a chance. That's why Enhanced C# tries very hard to be backward compatible with C#. I don't think 100% compatibility is necessary (or even possible, for people like me with very limited resources), but it's important to make sure the number of changes required to convert a large project to an EC# compiler is normally small. It's also really good, I think, to focus first on features that can be converted to plain C#. You see, if I was going to make my idea of the ultimate programming language, it would start completely from scratch and mostly ignore mainstream languages like C#, except for the purpose of consensus building (it's important to keep conventions that programmers are familiar with) and copying good ideas. I wrote about this recently in the blog. So to me, backward compatibility is non-negotiable [edit: unless you actually are interested in starting from scratch]. That said, I do believe in being more liberal about changes than C# itself. For instance, I've introduced a new Here are some specific points in response to your last message:
I haven't removed the
Also, references to class objects are also pointers. You don't require dereferencing those, right? Your thoughts on templates are interesting. It is certainly nice to be able to verify in advance that all possible instantiations of a template/generic are valid (if the type matches the method signature). But templates like you find in C++ and D permit greater metaprogramming - for example, you can do a It would be really nice to have non-nullable references.. have you seen the "Twisted Oak Studios" post on that? I have a sense that it's important to start work on an SIL (and MLSL) in order to have a solid foundation for converting code between languages. However, if possible I want to have some synergy between SIL and WebAssembly, so I've been waiting for the WebAssembly folks to make up their mind about what kind of primitives they want. WebAssembly, like Flame's IR, uses structured control flow, probably so that it is straightforward to polyfill to Javascript, but they haven't settled on which primitives to use. But hey, maybe the particular primitives aren't actually that important: code built on top doesn't necessarily need to target the actual primitives; I guess a language conversion framework ought to have some way to map any reasonable set of structured primitives onto any other. |
Beta Was this translation helpful? Give feedback.
-
I've tried to split your comment into separate points: Things that relate to D# and EC#
Thoughts on WebAssembly and SIL
These comments are starting to get absurdly long, and wasm and SIL seem - even though they fascinate me more than the practical differences between EC# and D# - somewhat off-topic here. |
Beta Was this translation helpful? Give feedback.
-
There is, of course, a well-specified algorithm for how the type of a lambda is inferred. I've read about it (though I can't recall where) and I'm sure complete details are in the C# spec. The basic idea is (1) figure out what the possibilities are based on context (e.g. based on available method overloads, you might see that the lambda must have type Okay, that definition of I like the arrow syntaxes better, because it means no large lookahead is required to distinguish between Rust, by the way, does use I'm not sure what you mean about template abuse in D's standard library. Do you have a citation for "RyuJIT will already constant-fold regular ifs on generic parameters"? What? You're not seriously thinking of making, say, The WebAssembly MVP is constrained to be "pollyfillable" - it has to map to asm.js, and they aren't able to support garbage collection under this constraint. I asked them if they had a plan for supporting garbage collection as a "first class citizen" in WebAssembly after MVP. One person responded:
Unfortunately the WebAsm folks are focusing mainly on making a C-like machine language for compiling "native", static languages. I'm certain they are not interested in making a highly interoperable platform like the CLR is, nor are they interested in defining a multi-language standard library. That's too bad, 'cause I think the software industry has a tremendous need for a way for languages to talk to each other on a rich, high level. The good news is that they understand the value of providing a very flexible platform, and they believe in the Extensible Web manifesto. While the MVP might not be a very good basis for "managed" languages since it won't have a GC, likely won't allow programs to generate code at run-time, and might not even have a resizable heap, in the long run I think it will get all those things. WAsm is going to be extremely important, I'm convinced of that. So, what we need to do is build an interoperability framework on top of it - a multi-language standard library, and ABIs for languages to talk to each other. The WAsm group itself won't make these things, but they are designing WAsm in such a way that an interoperability framework can be built on top. But who is "we"? I know I'd like to work on this, but I don't know about you. Sorry if this seems off-topic, but I thought we were talking about "all the topics". My goal in life is to improve the world, so if I thought I could do more good by dropping EC# and building something on WebAssembly, I would do that. I definitely want to publish a nice macro library first though - hopefully LeMP and its macros will be useful to some developers, even if EC# never gets a proper compiler. |
Beta Was this translation helpful? Give feedback.
-
Oh, I didn't mean to discourage discussing wasm/MLSL at all. I just thought that maybe those topics deserved their own issue, though that might have been silly of me in hindsight. Anyway, no need to lament EC#'s demise quite yet. Flame.Loyc and I would agree that WebAssembly is a golden opportunity for the development of a universal standard library. I would disagree that the time of either one of us would be "better spent" developing such an MLSL than developing Flame/Loyc/EC#/D#/other compiler and language tools, because such tools are very much a prerequisite for the emergence of any MLSL if you ask me. The creation of WebAssembly might very well create an opportunity for the emergence of new managed languages and tools relating to them, because the toolchains that do will probably support wasm at launch - mainly thinking of gcc and LLVM here - will likely have low-level languages such as C and C++ in mind. These C/C++ implementations will surely stick with their own standard libraries. A compiler for a high-level, managed language that targets wasm shortly after launch stands to gain more attention than one targeting, say, the CLR. A wasm standard library for managed languages would be equally unprecedented. This would give a wasm MLSL - if published at just the right time - the strategic edge it needs to boost adoption. All we need to AOT-compile a managed language such as EC# is a GC, which might just as well be the good old Boehm GC at first, which can be compiled with clang and then linked with the resulting wasm module. I'm interested in working on the following (probably in that order):
Does that sound reasonable and/or appealing to you? |
Beta Was this translation helpful? Give feedback.
-
When I think again about all the things I want to see accomplished, it strikes me how massive the task is. My wishlist includes:
This is too much to a couple of people to accomplish, even working full time (which I can't guarantee) and in fact, I know that if I were to do these tasks alone or even with one other person, I wouldn't get the highest-quality ideas. I feel like the right strategy is to build a community to crowdsource ideas, prototypes and code. I've known for some time that a community is needed, but I don't know how to build such a community, or even how to get publicity. I have to admit, I feel like I am quite busy these days even though I'm actually on vacation. Even the mundane stuff can be really time-consuming - I'd like to combine LeMP and its syntax highlighter into one Visual Studio extension for example, and show errors with red squiggly underlines, but I haven't found time to work on that. And I'd like to write an article about syntax highlighting in VS, because I've made it quite easy to write a syntax highlighter for a Loyc language in VS. And... well, nothing's coming to mind, but I'm sure there's lots of mundane tasks to do. Plus, I believe I just met my wife, and happily, I'll be devoting substantial time to her. I'd like to have a closer look at how you've combined EC# and Flame (and I'd like to know more about what Flame "is" and how it works) ... but I haven't found the time yet. I'd also like to know if you had any difficulties figuring out how to use EC# and Loyc trees - it was a big (and pleasant) surprise to see Loyc stuff suddenly integrated with somebody else's framework, with no foreshadowing in the form of "how-do-I-use-this" questions! Btw, are you using the EC# syntax highlighter and/or Custom Tool in Visual Studio? Are you using the latest version from Lib\LLLPG in this repo, or perhaps a regular build from So, yes, the four points you listed are appealing to me (assuming that I like the design of Flame, which I'm not yet familiar with). But if we are to have a chance of making serious progress, we'll need to figure out how to get more manpower. Do you have any ideas in that regard? Also, you'll need to write some sort of tour of Flame's architecture (giving the names of important classes and what they do), because I'm afraid I don't have time to figure it out by opening random source files (I'm too slow at figuring things out just by reading code). |
Beta Was this translation helpful? Give feedback.
-
My Loyc setup is pretty basic:
I'm sure there are lots of people out there who are interested in Flame, Loyc, WebAssembly and MLSL, but I have no idea how to make them magically appear here. Perhaps free cookies will do the trick? I am now also in the process of documenting whatever comes to mind about Flame's architecture. Is there anything in particular you'd like me to elaborate on? By the way: good news everyone, generic types have been implemented in Flame.Loyc as of today. The following alt class compiles successfully (though
|
Beta Was this translation helpful? Give feedback.
-
Hi, I've been busy finishing up an Esperanto quick reference for my girlfriend... I'd like to know more details about Flame's IR design - could you give a nontrivial example of some C# code and how it is represented in IR form? Have you considered mapping the IR to LES, as a way of communicating the content of an IR tree? And the type system - is it pretty much just .NET's type system? Is it C#-specific? |
Beta Was this translation helpful? Give feedback.
-
Awesome! Here are a few examples of C# code that generates an IR tree, along with a short introduction that addresses a number of design choices that I made while creating Flame's IR. Flame's type system is pretty close to the CLR's, though I think I'll revise it at some point in the future by making it rely more on reference equality (type equality comparisons are commonplace, but painfully slow right now), creating a "canonical" generic type/method instance type, and determining subclass/superclass relationships by virtual call. That should make Flame's type system more extensible and reduce implementation boilerplate code at the same time. Also, yes, I have been thinking of using LES as an interchange format for Flame. Here's my proposal:
Basically, I'd like to encode
which is then serialized as:
The major advantage of this template-based approach is that certain features, such as an empty attribute list and an id target node, are zero-cost in the binary encoding, without reducing the set of LES nodes that can be encoded. Also, this optimizes frequently used operations, such as Do you think that is something that can realistically be done, or would it require changes to Loyc's architecture? Did I miss any LES features? What do you think of this in general? |
Beta Was this translation helpful? Give feedback.
-
BTW, I forked Loyc and made some changes to better support Here's fecs Product.cs -repeat-command -terminal html Now: fecs Product.cs -repeat-command -terminal html Should I send a PR? |
Beta Was this translation helpful? Give feedback.
-
Why did you want to create a
|
Beta Was this translation helpful? Give feedback.
-
I'm just starting to read your document... here are my thoughts on the first code block
This code is odd to me in the following ways:
|
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
Okay. I made fecs Product.cs -terminal html -repeat-command EDIT: if you prefer carets over tildes, you can always just:
|
Beta Was this translation helpful? Give feedback.
-
Looking at the other examples I am uneasy about a couple of other things..
Why there is a distinction between 'statements' and 'expressions'? Experience has shown me that I don't want What's 'pixie' and |
Beta Was this translation helpful? Give feedback.
-
I have to admit, I don't understand very much of what you're saying about Flame and LES. This whole paragraph I'm simply not following at all:
And...
I was suggesting LES to be used as a format for communication purposes, i.e. between me and you and other developers, or between humans and computers, so that we have a well-understood, compact representation for the IR. A binary Loyc tree format and "index into a table for O(1) access" may be useful for something (although I don't know what your goal is), but it's not useful for communicating. |
Beta Was this translation helpful? Give feedback.
-
Personally, I enjoy (and have a lot more experience in) language design over back-end design and optimization passes. So if I was focused more on the core/middle and you were focused more on the back-end, it would make sense. You're right that fat pointers could (theoretically) be replaced with thin ones under global analysis, but (1) interface dispatch with fat pointers tends to be simpler than with thin ones (.NET optimized dispatch is complex), and (2) whole-program analysis wouldn't make sense in a system designed to replace the CLR - it's a dynamic system, and that's the way I think it should be. Part of the goal would be to get binary interop between programming languages, so global analysis isn't feasible (Wasm won't do dynamic linking in MVP, but it's a high priority). Plus, I'd like a language that supports "on-line" rewriting where debuggers or profilers or loggers can transform the code at runtime. I think there's a good chance that there's an existing system somewhere we should start from, rather than starting from scratch. Perhaps we should start from one existing back-end and one existing "modular" programming language (I don't recall seeing any languages before that were designed to be "modular", but I know that many languages consist of highly orthogonal parts, which is ... similar.) So we should be on the lookout for prior work. I don't have access to (closed) academic journals... but the web probably has adequate info. |
Beta Was this translation helpful? Give feedback.
-
Well, isn't Lisp a (more or less) "modular" programming language? It has macro support, its code is data, and its syntax is fairly regular. The features you're proposing (like run-time code rewriting) seem pretty neat. But they're also a lot of work, up to the point where they almost seem to necessitate a separate VM. Now, I'd sure like to build a VM, but to me it kind of makes sense to start small and focused; get the language itself right, before building lots of cool tools. How about one of us creates a prototype repository, and we start working on this thing's syntax? We'll have to at some point, so why not right now? Otherwise, we'll probably just end up in endless feature discussions without writing a single line of code. Plus, building a parser and a number of sample programs will likely give us a better idea of which features are useful, and which aren't. Also, I see that people are starting to notice LeMP. That's awesome. Congrats! |
Beta Was this translation helpful? Give feedback.
-
I have a name idea: "clasm": Common Language Assembly, a cross between the ideas of Common Language Runtime and WebAssembly. This word doesn't look popular on google (although someone already wrote "a Clojure library designed to convert jvm assembler into bytecode" by this name), and while clasm.com is taken, I could pick up the |
Beta Was this translation helpful? Give feedback.
-
I should have been more clear. LISP is traditionally dynamically typed. I want something that is statically typed, with a modular type system - so that new typing mechanisms can be added. Also, I really don't like s-expressions - ironically, though LISP theoretically has "modular" (or at least replaceable) syntax, everybody just uses s-expressions. I really want to get away from that. Could say more later, gotta go. |
Beta Was this translation helpful? Give feedback.
-
"clasm" sounds good, albeit a bit low-level. Though I guess that's kind of the point, right? Not a fan of S-expressions, either. It's easy to lose track of nesting. Maybe I wasn't entirely clear, though. I'm not advocating we create yet another dialect of Lisp, but I do think that some of its design decisions are worth considering, despite its syntactic flaws. What do you have in mind for a "modular" type system, implementation-wise? Something macro-like, like About that low-levelness. Do you want clasm to provide low-level primitives, and then expect users to build high-level macros, effectively making it both a low-level and a high-level programming language? Or do you want it to be an actual assembly language that is targeted by compilers for high-level programming languages? |
Beta Was this translation helpful? Give feedback.
-
See "typing according to Qwertie" - I'm interested in a layered type system. So yes, it would have low-level primitives and high-level macros and typing modules, making it low-level and high-level language at once. I do not envision ordinary users creating type-system modules or even their own macros, but that several parties could contribute parts of the language. At the lowest level, perhaps there needs to be a built-in notion of physical types - the language would define a type system required for memory safety, but it should be fairly unconstrained. For instance, a structure like
Could be seen as 12 bytes of "don't care" (for memory safety they don't matter) and 4 or 8 bytes of Other ideas of type would then be added as layers, with an unlimited number of types per value. I have no idea how to take the mathematical approach, where a type system could supply "proofs" of correctness, so instead we'd probably just let type system modules do "transmutes" (unsafe casts) and worry about their own correctness. At least typing modules that don't do transmutes should be guaranteed memory-safe (if not necessarily correct). I imagine three kinds of typing, and each kind could have multiple modules:
Typing that is concerned with memory safety in the face of allocations and deallocations, i.e. lifetime analysis, has elements of the first two (it's "physical" in that it provides memory safety and "semantic" in that it does not affect code generation or even care about memory layout). I expect other modules may be multi-category, e.g. if a module merely requires proof that an int is in range 0..100, then it's purely kind (2). On the other hand, if two functions can be overloaded based the different integer ranges they accept, that's more of a (3) thing. My idea is nebulous. We should look for prior work. One thing is, I want to write some software in Rust to get a better feeling for how it solves things, then think about what can be changed to increase its flexibility. |
Beta Was this translation helpful? Give feedback.
-
Other research TODOs here. One thing I want to stress is that the different notions of type can be independent. For instance, suppose a value is measured in metres. In a traditional type system you'd have either But when you see different ideas of type as independent, each one can have its own system for "solving" the type of each value has. For a unit system with types like |
Beta Was this translation helpful? Give feedback.
-
That's a really cool approach to typing, actually. I wouldn't include a lot of primitive typing disciplines, though. Over-designing the type system for a few specific scenarios, like Rust's lifetime system, would make the type system less flexible, in my opinion. Typing modules should just have the right tools to do that kind of thing themselves. There's something you wrote that I don't quite understand though: how could Thumbs up for type inference. Powerful type inference makes programming fun, in my opinion. But if we're doing type inference, then I suggest we get rid of function overloading sooner rather than later. Type parameters are far more effective at achieving compile-time polymorphism than overloading, especially when they're combined with sufficiently powerful constraints, like typeclasses or interface constraints. Constructors may be an exception to this rule, though, and I'm unsure how those should be handled. Perhaps a new object construction paradigm is warranted? Implicit conversions also complicate type inference, though they can be pretty useful at times. On a side note: I implemented Hindley-Milner type inference a while back. Is your unit inference algorithm similar? |
Beta Was this translation helpful? Give feedback.
-
It seems to me that a certain amount of "primitive typing disciplines" (if I understand your meaning - and I don't necessarily) are necessary to let the other type checkers build on top and enjoy memory safety. I suspect Rust's lifetime system doesn't reduce flexibility, because... imagine a program that wants to just use garbage collection and not worry about lifetimes. GC objects can have a lifetime of Using the notation
There ought to be some way to specify that Unit inference is only like Hindley-Milner in the general sense that you scan the program figuring out types as you go along using some unification rules. Again, I'm proposing unit inference, NOT general type inference. I know general type inference doesn't get along with overloading, but overloading is really useful so ... I'd have to think long and hard before removing it. You may be right, but I want to get experience with Rust first (to better understand how they deal with overloading being missing). I've used Haskell for a few months, but that language is so wacky, it seems to occupy a different part of my brain and I'm not prepared to apply it to "normal" language design. Rust is nice because it bridges the gap. Most likely some basic overloading could be allowed in any case (e.g. different numbers of arguments). I don't think unit inference needs to care about overloading: the unit checker could simply run after overloads are resolved (though this does imply that you can't overload functions based on unit types). |
Beta Was this translation helpful? Give feedback.
-
I would point out that in the kind of language I'm proposing, it could have both type inference + overloading disabled AND no type inference + overloading allowed. After all, the coexistence of substantially different type systems is really the goal here. It's just that presumably we'd have to pick one system to be the default. But Loyc is about interoperability, and I still think that "one compiler, multiple languages" could be a fruitful approach to interop. Of course, we'd need to solve the problem of how to allow the language-with-type-inference to call arbitrary overloaded functions... from the user's perspective, I think he'll just need to be more explicit about the types of the arguments to the function, so that overload resolution works. |
Beta Was this translation helpful? Give feedback.
-
Actually, even if implementing the Rust lifetime system as a built-in feature doesn't force anyone to use it, I'd still rather figure out some way to provide low-level type system hooks. A Rust-style borrow checker could then be built on top of those hooks. But I also expect to be able to implement a GC, or Go's local variable references, on top of those hooks, without built-in support for either. I wouldn't dismiss the possibility of applying Haskell to "normal" language design out of hand. In my understanding, typeclasses are just like Go-style interfaces, which are instead used as generic constraints. They avoid the pain of copy-pasting the same function over and over again for all (numerical) types. They're also the moral equivalent of Rust's traits. If one ignores multi-parameter typeclasses for a minute, then what's not to love? I'm a little puzzled as to why you'd want unit inference as a core feature which is on-by-default, but not type inference. Even Rust has type inference, and I can't say I've ever used units in F#, which has unit inference. C++ has units, too (no unit inference, though), and I've only used them once, because the standard library forced me to. C++ units were more of an inconvenience, really. That's just anecdotal evidence, though. Splitting these ideas up into a type-inferred language and an explicitly typed language would make sense, I suppose. And interop between those two languages would then probably require some thought, as well. But let's not get ahead of ourselves here. Building one awesome programming language should be challenging enough for now. Perhaps a list of MVP-ish features for said language would be useful. It'd reveal which features are priorities and which ones aren't. |
Beta Was this translation helpful? Give feedback.
-
I'm not sure what you mean about using "hooks" so that Rust-style lifetimes are not a core feature... but whatever you're thinking of sounds good. I'd need to see a more detailed and concrete design sketch. Actually, Haskell type classes are very much like Rust traits, and not like Go-style interfaces. As I understand them, Go-style interfaces are more flexible than both. I think this flexibility limitation has a name in Rust: "trait coherence". I'm having trouble finding a definition of this term though. And in fact, these concepts aren't fully clear in my own mind (that's part of the reason I want to work with Rust for awhile.) I don't want unit inference as a core feature. It is merely an example of a type system module that could be written. It is a useful example to show how different concepts of type can be independent. It is worth noting that if a unit inference module exists, it should be on by default, because unit information should be stored in compiled DLLs for interop purposes. For example, a unit inference engine will detect that the function
Has a polymorphic unit type of Sigh. F# ... I've never been able to get used to it. The OCaml subset I can handle, but things like writing constructors, and other OO stuff, is way too hard and confusing in F#, I just fought with the compiler constantly and the syntax of everything was baffling. So I never got as far as trying out its unit system. Whether units are worth using depends on how easy it is; in many languages, units are a burden because they are overly infectious - when you start using them, the compiler starts requiring unit annotations all over the place. In languages like C++, units are definitely not worth using - the language just isn't designed for units, and you only get unit checking, which is horrible compared to unit inference. I don't know if F# makes units friendly enough or not. I don't know if I was clear. I'm not saying "let's build a language with two type systems". I'm saying "let's build a language with unlimited type systems - two of them might be like these". And these type systems wouldn't be monolithic and totally separate, but modular, and sharing parts between them. Among the things I want to figure out are (1) how to allow interop between type systems that are incompatible in some ways, and (2) what kind of type system is the most flexible, so that it accommodates or "subsumes" the type systems of most existing languages. Now, in order to answer these questions, I think it would be instructive to actually try to implement two type systems that are traditionally considered incompatible, and see what we can learn from that experience. |
Beta Was this translation helpful? Give feedback.
-
Why would you say that Go interfaces are more flexible than Haskell typeclasses? Granted, Go-style interfaces don't have to be implemented explicitly, but in my understanding, typeclass instances can be declared pretty much anywhere. So - like Go-style interfaces - typeclasses don't impose the same implement-at-definition limitation that traditional interface types impose. Oh. I see. I thought you wanted to make unit types built-in. I was confused. Sorry about that. Your example is interesting. It seems to imply that one entity can have more than one "type." Seems promising. Looking at different type systems and figuring out some way to unify them sounds like a good approach. Which type systems would you suggest we implement? |
Beta Was this translation helpful? Give feedback.
-
Let's continue in #26. |
Beta Was this translation helpful? Give feedback.
-
I think I should rename this repo to I finished the LeMP reference manual BTW. I was also thinking of creating an organization called Loyc. Unfortunately there is a problem: there is already a user named Loyc ... with no repos, no commits, no activity of any kind. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Continuing discussion from #3.
Beta Was this translation helpful? Give feedback.
All reactions