[HN Gopher] Unison Programming Language ___________________________________________________________________ Unison Programming Language Author : gautamcgoel Score : 270 points Date : 2021-06-27 16:05 UTC (6 hours ago) (HTM) web link (www.unisonweb.org) (TXT) w3m dump (www.unisonweb.org) | sdfjkl wrote: | > Run ucm init to initialize a Unison codebase in $HOME/.unison | | Ugh, this conflicts with my favorite file sync tool: | https://www.cis.upenn.edu/~bcpierce/unison/ | carapace wrote: | Unison (the file sync tool) is so awesome! | zepearl wrote: | It's great when it works, but different versions of <OCAML? | and/or Unison?> on different hosts/VMs screw it up. I never | understood the real reason why (but I never really | investigated, lazy) => it's a pity. | barnyfried wrote: | This is stupid | thom wrote: | Super interesting, I was thinking about append-only codebases | very recently: | | https://news.ycombinator.com/item?id=27492727 | | The implications of this, with the right frameworks and | processes, seem potentially huge. | joe_the_user wrote: | The thing is, the closest thing to append-only you have with | ordinary programming languages APL and it's variants, where you | can construct powerful functions with powerful primitives and | methods for combining them. | | But the thing is that APL quickly becomes a "write only" | language - as far as I know, the main use of APL someone | sitting at a brockerage who can cobble together any algorithm | on twenty minutes and often throws away the result afterwards. | | Which is to say, Unison is interesting because it seems to | underestimate the importance of a program's code as _document_ | , as complete, coherent, human-readable, single-view, | intentionally created text. Why hasn't the stream of ascii been | replaced as the format of program in the last twenty years? | It's a good question but the answer isn't that it's just matter | of conservatism. There are several other things involved. | acjohnson55 wrote: | It doesn't discard the text. The text, like the documentation | and comments, are stored and re-rendered during editing. It's | fundamentally textual. It just doesn't have to be stored as a | one dimensional text stream. | ajuc wrote: | This is like maven at function level with no SNAPSHOT versions | allowed :) | | How is recursion handled? To get a hash for a function you have | to have hashes for every function it calls. Is there special | "recurse" opcode? | | And how do you update a function implementation when you have a | cycle in the callgraph? | refried_ wrote: | Yes to Maven, and good questions. | | Simply put, there is a special recurse opcode. | | When you have a cycle in the call graph, they all get hashed | together as a single unit; you update them together as a single | unit too. | umvi wrote: | I had a really hard time wrapping my mind around this just | reading the website alone. If you are in the same boat, watch the | first 10 minutes of this video at 1.5x speed: | https://www.youtube.com/watch?v=gCWtkvDQ2ZI | | and it will make so, so much more sense. | | ...and if you are like me you'll probably need to read this | twitter thread to get the answer to your #1 question: | https://twitter.com/unisonweb/status/1173942974381744134 | | Basically the core idea (or one of the core ideas) is instead of | a function (like fib(n) which returns nth Fibonacci number) being | identified by its _name_ (fib) as is the case with most | traditional languages, it 's instead identified by a hash of its | implementation. | tgbugs wrote: | Interesting. You can write a macro and some buffer modifying | code to do this in elisp. But having now written up the rest of | my response, why not just use Smalltalk? | | The hard part is coming up with the normalization routine which | guarantees that (lambda (a) b a) == (lambda (b) a b) and coming | up with the rules for statement reordering for top level and | internal definitions so that you can identify semantically | equivalent statements where the outcome is order invariant. | This is critical for making the hash functions useful and I | suspect preventing denial of service attacks on the human | brains that have to audit the code. | | Being able to write a version of the code and then do the | equivalent of creating a package.lock file to crystallize the | hashes seems like a reasonable workflow. This probably winds up | being easier in common lisp though since you can put the | crystallized implementations in their own packages. | | You could also view this as a kind of extreme type theory where | every function (with regular names) has the type of its | normalized representation (compacted to a hash for sanity's | sake) and then you can run the checker to see if the | types/hashes have changed. If you have somewhere that keeps | track of every hash that a function with a particular name has | had then you could automatically refactor, or could even | support having multiple versions of the function with the same | name used in a program at the same time. I'm not sure how users | would feel about having to carry around `(funcall ((with-norm- | id '(lambda (+ a b)) f)) a b)` though ... probably just give up | on editing the textual representation and go back to the image | based approach of Smalltalk and Interlisp where you can hide | the hashes. | | Will be interesting to see how Unison evolves. | lisper wrote: | Sounds like a cool idea, but how do you fix bugs in functions | with lots of callers? | umvi wrote: | That's basically what the twitter thread I linked explains. | It sounds like there is an automatic propagation mechanism | for updating downstream callers if the type hasn't changed, | otherwise it sounds like a manual update process. | torginus wrote: | Sounds like trading one set of problems for another. | capableweb wrote: | Welcome to software engineering where there is no golden | bullets, only different tradeoffs :) | aparsons wrote: | Law of conservation of complexity | acjohnson55 wrote: | The same way we do now: release a new version and tell people | to migrate. | lisper wrote: | No, now I can just redefine the buggy function and all the | callers will get the new version automatically. Having to | update all callers seems like a high price to pay. Seems | like the Right Answer is something like a hash of the api | or the contract rather than the implementation. | ucarion wrote: | The (public) name of the implementation is the unique | identifier of the contract in most systems, so I think | your "Right Answer" is roughly the status quo? | lisper wrote: | No. The name of a function in current languages has no | connection at all to what the function does. (What would | be the contract for a function named 'foo'?) | gbhn wrote: | That's kind of the thing that makes APIs possible, right? | It sounds to me like "what if programming were done in a | completely flat global namespace in which abstractions, | encapsulation, and structure were impossible." | lisper wrote: | No. An API specifies more than the name of the function. | It will specify the arguments, their types, the type of | the return value, and at least informally, what the | function does. You can change the underlying | implementation without changing the API. That's the whole | point of an API. The problem with current API technology | is that the informality of the spec of what the function | does. That allows some aspects of the behavior of the | function to change without triggering any warnings. | | By having the linker work on hashes of implementations | you eliminate that problem but create a new problem. You | can no longer change the behavior of the function because | you can't change the function. That means you can't | suddenly change behavior that some caller is counting on, | but it also means you can't fix bugs without changes in | the caller. | ajuc wrote: | Nope, according to | https://twitter.com/unisonweb/status/1173942969726054401 | | when you change a function implementation the system has | to walk the callers graph backwards starting from all the | places where the function was called updating all the | implementations with the new hash, then callers of these | with the new implementation and so on up to main (or | whatever it's called). | | I had a chance to implement something like this in a | system that used jbpm 3 graph language (basically process | X version 1 called process Y version 1 and I updated | process Y to version 2). It's nontrivial especially with | recursion, I'm wondering how they are dealing with that. | lamontcg wrote: | A git-like datastore for your AST+callgraph. | ajuc wrote: | Let's say you have definitions like that: | f: Nat -> Nat g: Nat -> Nat h: Nat -> Nat | h x = g (x * 2) g x = f (x * 3) f x = x < | 0 ? 1 : h (x / 4) | | And now you change f to be f x = x < 1 | ? 1 : h (x / 4) | | How do you do that? There's a cycle in the callgraph. In | fact - how do you calculate a hash of a function that | calls itself if you need its hash to calculate its hash | :) | | EDIT: nevermind, recursion is a special case handled | differently. | dcposch wrote: | Cool to see people thinking this big! | | One challenge I foresee is unintentional coupling. Say you have | two functions: | | func serialize(MyRecord) ... | | func debugToString(MyRecord) ... | | Now if you ever make the mistake of having giving those the | same implemention, then in Unison they'd be the same hash | reference, right? | | Then if you want to update, say the debug print later it would | update _all_ callsites for that hash including the ones that | originally called serialize(). The two are no longer | distinguishable. | sgk284 wrote: | The names are just pointers, and they're both pointing to the | same definition in your example. But when you redefine one of | those, you would point one of the names to a new definition. | | It's similar to how DNS can have two domains point to the | same IP, but then you can change one of those domains point | to a new IP. | ajuc wrote: | > The names are just pointers, and they're both pointing to | the same definition in your example. But when you redefine | one of those, you would point one of the names to a new | definition. | | But how do you know which name was called where if the | callers referenced the content hash not the name? | milansuk wrote: | I also think that the DNS analogy is wrong because all | callers are hash-based. The only solution I see is to go | through the list of all callers and manually update | selected ones. | | If I understand Unison right, the names are used only on | the developer's layer(to write code), but when you save | code, it's all hash-based. | | Still, Unison got my attention. | aparsons wrote: | Would it not be correct for those callers to keep calling | the old (shared) implementation? | ajuc wrote: | well it would be nice to have a way to update old code | acjohnson55 wrote: | It knows what name you intended to use, because that's in | your source, so I'm pretty sure it isn't a problem if | implementations converge and diverge. | andi999 wrote: | Cool, but why/what for? | umvi wrote: | I'm just as much of a novice as you, but one of the use cases | the creators had in mind are distributed computing systems. | For example, if you have to crunch a bunch of data in the | cloud, you would write your data crunch function/algorithm | (which is represented by some hash '#asdjh238ad') then spin | up nodes to crunch data using '#asdjh238ad'. When a new node | in a cluster comes up it can say "I don't have '#asdjh238ad'" | and the orchestrator or one of the node's peers can send over | a copy of it. | | With a traditional programming language you couldn't do this | because "send me a copy of sort()" would be met with "which | sort()?". Whereas with unison every different sorting | implementation would have different hash, so there would be | no confusion. | Nullabillity wrote: | That makes sense as a build system (and is more or less how | Nix works). The question would be why you'd subject your | _source code_ to this. | mst wrote: | there was a paper that implemented an r7rs compatible | module system for termite scheme that used hashes for | identification for netework transfers of code but left | the source files still normal - I think focusing on the | textual representation too much misses the point a bit | here. | torginus wrote: | I'm not totally convinced by this. | | - Storing the AST on the disk in a million files is not | necessarily the best use of the filesystem. In contrast, most | languages store text files on the disk, and build up a similar | AST in memory only | | - You can't view your code without special tools, which means | all text editors/version control etc. need to be Unison-aware | | - Since the language is append only, all edits look like | additions in version control | | - Their solution for the diamond problem (depending on multiple | versions of the same library) is having hard dependencies on | exact versions, and including both copies can be at best | wasteful, at worst bad (what if v2 fixes a bug that was in the | v1 dependency), I think this is a hard problem, and the reason | why semver exists | | - As others have mentioned, the append-only nature of the | language makes bugfixes difficult | | - Solutions that dynamically discover code dependencies and | automatically run tests exist for both procedural and | functional languages | | - Detecting that 2 things are the same through hashing is | nontrivial, can it detect that 1 + x + 1 is the same as x + 2? | The ASTs are different | asoltysik wrote: | > Storing the AST on the disk in a million files is not | necessarily the best use of the filesystem | | A new codebase format just uses a sqlite database instead of | a million files | | > Since the language is append only, all edits look like | additions in version control | | Traditional methods of showing change in verson control, that | is text diffs, don't make sense here anyway | | > Detecting that 2 things are the same through hashing is | nontrivial, can it detect that 1 + x + 1 is the same as x + | 2? The ASTs are differen | | It can't detect that. It if could it would be pretty cool, | but I don't think it would improve the usability too much | the-smug-one wrote: | What's the point of detecting that 1 + x + 1 is the same as | x + 2 anyway? If I wrote it in one way, I meant it to be | that way for a reason. Should it also be able to prove | arbitrary code is semantically equivalent? Well, it can't | do that for obvious reasons. | lamontcg wrote: | Loading both copies of a library can be very useful to deal | with the situation where one piece of code has been ported to | v2 (due to bugs/features or just generally keeping up with | updates) and another piece of code is hard blocked on the | v1->v2 migration because it is much more costly, and its | possible that v2 is actually buggier for that other use case. | There's a bit of a naive idea that software always gets | better for everyone and that projects have an infinite amount | of spare time to drop everything to bump dependencies. That | feature is actually very useful. | | (Which is not to defend the rest of the append-only | immutability of the rest of the language, that looks a bit | whack -- but then I've seen whack stuff get wildly popular, | so I have no idea -- but while having 2 versions loaded at | the same time might be useful I'm not sure I want to deploy | every version that has ever existed that smells way too | bloated) | torginus wrote: | You are right - but choosing the correct solution imho | needs to be done with human oversight - I think a semver | based dependency resolution works great here, for example | if bar requires foo 1.0 and baz needs 1.0.1 they will | happily use the same version, but if baz used foo 1.1 they | would use the separate ones. | lamontcg wrote: | Except 1.0.1 can fix a bug that one piece of code needs, | while another piece of code can be happily bug dependent | upon it. | | You can scream at the developers that they've violated | semver but a "bugfix" is entirely subjective (relevant | xkcd, spacebar heating, etc). | | And even when developers violate semver in a point | release the problem still exists. They actually rarely, | if ever, rollback with a 1.0.2 that is equivalent to | 1.0.0 and instead usually move forwards. | | And if you have a language that supports loading 1.0 and | 1.1 then there's no point in being artificially | constrained over which two versions can be loaded at the | same time based on the label, the underlying framework | shouldn't be built to care. There's no need for a multi- | version library loader to care about what a bugfix is. | slver wrote: | SemVer remains a pragmatic approach that works in vast | amount of cases. It's unclear what alternative we have | here which works in more cases. | lamontcg wrote: | We don't have any better alternative, but lets not be | naive about it when it comes to building bits of | framework. | | Semver would just be an artificial impediment at this | level. | slver wrote: | SemVer is an impediment only if you insist to make it so. | fastball wrote: | I think another key idea is that you're still thinking | about libraries as complete packages where you kinda | install two versions of the same thing. But it seems more | likely in the Unison ecosystem that you'd end up with the | ability to much more easily only extract the specific | functions you need. | | So say there is v1 and v2 of a utility lib in my dep | tree, but actually only using func A from v1 and func B | from v2. Then I just have the AST of v1.A and v2.B in my | deps and everything works. | Nullabillity wrote: | You still need some unit of atomicity to be able to | maintain invariants. You can't combine | HashMap_LinearProbe::insert with HashMap_Chains::remove, | because they both depend on implementation details in | order to maintain HashMap's invariants. | jackcviers3 wrote: | Semver doesn't help in the case of transitive binary | incompatibility. If lib A depends on B v2, and lib C depends | on B v1, and application D depends on A and C, you cannot | load a version of B that satisfies D, A, and C. Semver tells | you that B 1 and B2 are incompatible, but not how to solve | the issue. | | Unison solves the issue - there isn't any binary | incompatibility, because the transitive versions of Bv1 and | Bv2 cannot be in conflict - the function references are to | guaranteed unique and different versions of the art. | | As for bug fixes - you can specify in your code exactly which | version to use. | | As for editors needing to be unison aware - they just | delegate everything to the compiler via lsp and bsp. | | Bug fixes are no more difficult than making the change. A new | version is created, and your code can now depend on it. Old | code will still run off of the old version. It's up to the | code owner to decide to use the new, but fixed version. | | Version control is all handled in the language itself. | | As for the hard hashing problem... Runar is a particularly | intelligent individual. I expect that his algorithm works | pretty well. | | The first argument about storing the ast is moot in an age | where cached compiled typescript, Python, and .class files | take up inordinate amounts of disk space. | | > Solutions that dynamically discover code dependencies and | automatically run tests exist for both procedural and | functional languages | | Eh. Piping and yarn ain't got nothing on maven and ivy and | apt. But yes, dependency management isn't anything new under | the sun. Dynamically resolving individual function versions | in packages alongside binary incompatible functions is. | infogulch wrote: | I think Unison paired with a strong graph database instead of | the filesystem would be a powerful combo. It would very | naturally represent the AST graph directly and would benefit | from graph db optimizations. The cost would be the need to | invest a lot in new tooling: you'd want a graph db-based | source control implementation that offers similar | cryptographic certainty to git; you'd have trouble using | existing tooling directly like text editors that expect files | on disk; etc. | mpweiher wrote: | > identified by a hash of its implementation | | Sound a lot like darklang. | | Like others, I am dubious about this being in any way a useful | feature. Separating implementation from name (/interface) and | binding to that interface/name instead of the implementation is | one of the fundamental and _useful_ parts of abstraction. | brundolf wrote: | > Unison definitions are identified by content. Each Unison | definition is some syntax tree, and by hashing this tree in a way | that incorporates the hashes of all that definition's | dependencies, we obtain the Unison hash which uniquely identifies | that definition. | | Very cool core concept. Reminds me of some things Rich Hickey has | said about the idea of versioning dependencies at the function | level | | That said: I wonder if this idea would make more sense as static | analysis on an existing language. It would have to be trivial to | enumerate all code that might influence a function's behavior; so | something totally pure like Haskell or Elm | iamevn wrote: | it's really neat, I love how easy it is to search for functions | by type to find what you need. | | The one thing I ran into (as someone who only vaguely knows | haskell) is that it seems like it's impossible to write a | function that takes a list of A or B as an argument and then | branch on the type of each element. I can use Either but then I | need to decorate each element in the list with Left/Right rather | than just use their types. | | This is probably just not how things work in Haskell and I just | need to be okay with that. | creata wrote: | > This is probably just not how things work in Haskell and I | just need to be okay with that. | | Yep, that's just how things work in Haskell: disjoint unions | are much simpler regular unions, and they're usually what you | want in the first place. I think it'd be nice if Haskell had | automatic conversions between types (so a and b can be turned | into Either a b implicitly, with an error if a = b) but I don't | think there are any plans for that. | JackMorgan wrote: | Perhaps are you looking for Sum Types?[0] They let you group | several types into a unifying type, e.g. a Shape is a Circle, | Square, or Triangle, then you can use pattern matching to have | different behavior for each. This example is in F# [1] but it's | almost exactly the same as it would be in Haskell. | | [0]https://www.schoolofhaskell.com/user/Gabriel439/sum-types | [1]http://deliberate-software.com/christmas-f-number- | polymorphi... | __david__ wrote: | This looks like a neat idea--I can see upsides and downsides, but | would have to experiment to see if one outweighs the other. | | One thing I didn't see in my (admittedly quick) perusal of the | tutorial and faq: what is the technique to run a Unison program | from the command line? Is it practical for making unix cli tools | (yet)? | 0_gravitas wrote: | I've been semi-closely tracking this project for a while, and imo | it's easily __the__ most interesting project I've seen in the | sphere period. Serendipitous-ly, I came across an interview a | couple weeks ago with one of the main bodies behind the project | on the Corecursive podcast (from early 2019) (I think their name | was Runar Bjarnason). Had no idea until it was mentioned almost | offhandedly in the last few minutes! | agbell wrote: | I think this is the episode you are talking about [1] Runar and | Paul a huge inspiration! I'm not totally sold on this idea as | practical, but I think it will get there and while they have a | lofty goal, I certainly wouldn't bet against the pair of them. | | [1]: https://corecursive.com/027-abstraction-and-learning-with- | ru... | prezjordan wrote: | Strongly encourage anyone reading this to take 20 minutes to | download ucm and run through the Getting Started guide: | https://www.unisonweb.org/docs/quickstart/ | | Programming with a codebase manager and a scratchpad is just so | much fun - I found myself hypnotized and came back an hour later | with some janky min heap code. Definitely seems to scratch an | itch for me. | dang wrote: | The past threads appear to be (others?): | | _Unison: A Content-Addressable Programming Language_ - | https://news.ycombinator.com/item?id=22156370 - Jan 2020 (12 | comments) | | _The Unison language_ - | https://news.ycombinator.com/item?id=22009912 - Jan 2020 (141 | comments) | | _Unison - A statically-typed purely functional language_ - | https://news.ycombinator.com/item?id=20807997 - Aug 2019 (25 | comments) | | _Unison Language March Update_ - | https://news.ycombinator.com/item?id=19528189 - March 2019 (1 | comment) | | _Large-scale, well-typed edits in Unison, and reimagining | version control_ - https://news.ycombinator.com/item?id=9708405 - | June 2015 (11 comments) | | _Unison: a next-generation programming platform_ - | https://news.ycombinator.com/item?id=9512955 - May 2015 (128 | comments) | janjones wrote: | There is nice blog post summing up what's cool about Unison[1] | | [1] https://jaredforsyth.com/posts/whats-cool-about-unison/ | PaulDavisThe1st wrote: | Good explanations, but I'm always a little suspicious when I | see things like this: | | > Code is stored as a structured, type-checked tree in a | database, not as text in files | | What does everyone think a filesystem is? | thethimble wrote: | There's an important distinction between how non-unison code | is stored (literally as plain text files which must be re- | parsed and re-compiled every time) vs how unison code is | stored (as a post-parsing data structure). | | The file system is in an entirely different and irrelevant | layer of abstraction. | turtletontine wrote: | I'm not totally sure what the important distinction is | here. For many languages the important thing is already a | post-parsing data structure, that's what any compilation | output or byte code is. You obviously want to keep the raw | source around as well if you're the developer. Nothing new | about having separate source code and compiled formats? | turtletontine wrote: | Update: I'm skimming here | (https://jaredforsyth.com/posts/whats-cool-about-unison/) | and here (https://joshondesign.com/2012/04/09/open- | letter-language-des...) and I see Unison is serious about | not having raw text source code as the ground truth. I'm | intrigued but don't totally understand yet. | | I'm sure this analogy is technically incorrect but: This | reminds me of Smalltalk and old Lisps on mainframes | shared by many researchers where the main thing was the | VM image, not an object file. Though the probably kept | the source code around? At a gut level getting rid of | source code makes me uncomfortable but I'm ready to learn | more. | | PS sorry for the ugly raw links I'm on my phone | jack_h wrote: | Perhaps see my reply here | (https://news.ycombinator.com/item?id=27654225). | | I think you may be misunderstanding what is being stored | here. Now as a caveat I'm not familiar with this | language, but I am familiar with the concept as | described. They are not removing source code, rather | source code is stored after some processing; in this case | it appears to be after lexing, parsing, and type | checking. I'm not sure exactly what is being stored, i.e. | an AST, but it sounds like they're basically moving this | stage of compilation/interpretation to be much earlier in | the process. | | I'm assuming this database can be queried and the result | can be rendered back to a textual presentation as well. | Presumably this opens the door for syntax being divorced | from language semantics since how the syntax is parsed | into the database and how the database is rendered into | text can be a client side decision rather than set in | stone inside the compiler/interpreter. What is set in | stone is the semantics of the database that everyone must | agree to. | | Again, there's the caveat that I'm not familiar with how | this language in particular is implementing this concept. | jack_h wrote: | I'm not sure I understand your question. Could you elaborate? | jack_h wrote: | Storing code in a database is super cool stuff and is something | I've been thinking about for a number of years. I'm actually | surprised this development hasn't happened sooner since | basically all tooling is forced to deal with the limitations of | storing source code as text. | | The article gives an example that most programmers would be | familiar with; canonicalization so that version control and | code reviews go smoothly. Version control also becomes somewhat | simpler as it can compare the structure of code rather than the | structure of a sequence of characters that still must be lexed, | parsed, etc. There are lots of other areas where storing code | in a structured database of some sort would benefit tooling as | well. One example is the use of language servers to index, | perform continuous recompilation, perform cross-reference | lookups, and offer code completion. With a structured database | a lot of this becomes relatively trivial. | | I'll definitely have to look into this language further as I'm | curious about how their database is designed. | grawprog wrote: | Thank you, that helped explain pretty well what abilities are. | I felt like I was kind of starting to get what the language was | about, then I hit the abilities section and I had no idea what | it was talking about. | jiaminglimjm wrote: | Programming language i18n. | michael-ax wrote: | I'd love to see this in c, for scheme/racket. | jjfeiler wrote: | The core idea here, that of hashing the ast of a function, is | similar to what Maple V from Waterloo Maple Software was doing | circa 1991 when I last used it. | dthul wrote: | An immediate caveat I came across: if you want to look at some | Unison code you need a special code management tool. Take for | example their base library on Github: | https://github.com/unisonweb/base | | The actual code lives in a sqlite file in the .unison/v2 folder. | That would mean existing tools like version control and editors | would need to learn about how Unison works in order to seamlessly | support it. Also pulling out code into a scratch file, editing it | and pushing it back into Unison's database sounds kind of | annoying. Again, this could probably be solved with an editor | that would make this process more seamless and feel more like | editing regular code. | | As it currently stands it seems very cumbersome to use, mostly | due to the tedious process of even just exploring a codebase, | nevermind modifying it. | imtringued wrote: | No, they just need to use FUSE and provide file system level | access to the source code. | dthul wrote: | Yes, I thought about something like that. Being able to map | it to the filesystem and back to the Unison database. | | But then, what is the point of this content addressed code | again? What do we gain from it that we don't already have | now? With current file based version control you already have | an append only repository, code is never deleted from the | .git directory, it's just not always mapped to a file in the | source code directory (until you check out an old revision, | that is). | | Edit: I guess Unison still has the unique feature that | dependencies are referred to by identity and not name. | brundolf wrote: | This is an interesting choice. Any language or framework has to | make a dozen or more choices between doing something in a way | that's compatible but compromising, or bespoke but... bespoke. | It's always a painful choice in my experience. This one is | particularly bold, though. | adrusi wrote: | It's afaict a necessary decision, since unison is designed | around the possibility of having multiple versions of the | same function referred to be the same name. | adrusi wrote: | FWIW because of how unison works, you get a lot of the benefits | of version control without using any proper version control. | Probably for small, single dev projects version control would | just be redundant. | | That's not to say this isn't a limitation the project will need | to overcome to be useful, just a caveat. | zawodnaya wrote: | In practice it's a lot less annoying than navigating a file | hierarchy and looking in text files that have a lot of things | other than what you're looking for. | | See also https://share.unison-lang.org/ where you can look at | the base library, and some (contributed?) libraries as well. | dthul wrote: | I agree that text files might not be the best way to store | code. My point was more that all of the existing tools like | code editors and version control systems have been designed | around the concept of files though. And instead of Unison | being able to tap into the existing ecosystem of tooling, | they have to rebuild custom versions. Maybe there would be a | way to map a Unison codebase onto the file system and back? | | Edit: also worth mentioning that thanks to specialized | editors you don't need to manually browse through files but | you can browse your code similarly to https://share.unison- | lang.org if you so please. That's another plus point of the | vast existing ecosystem, it already offers so much and it's a | shame that Unison can't make use of it (at least for the | moment). | wyager wrote: | This looks pretty well done. It doesn't seem like a gimmick; | they've made a lot of good choices beyond the core conceit of | content-addressable code. | | One thing I didn't see skimming the language reference page: is | there any sort of typeclass mechanism? | refried_ wrote: | No, but it's planned; probably in the form of implicit | parameters. | auggierose wrote: | Submission inspired by | https://news.ycombinator.com/item?id=27651197 , I guess | WillDaSilva wrote: | They mention using git to version Unison code, and point out how | there'll practically never be any version conflicts because of | the immutable / append-only nature of the language. | | Doesn't that mean that the git repository will only ever grow, | and that old code will stick around forever? I hope I'm | misunderstanding because that would be unfortunate if true. | teraflop wrote: | Isn't that true of any Git repository? The internal object | store keeps every version of every file that has ever existed | (unless you rewrite history). | | In practice, Git's content-addressable storage and delta | compression make it work fairly well for all but the largest | repositories. | dthul wrote: | What I don't understand is what they do when merging two | branches. If both branches introduce a function with the same | name a merge conflict is inevitable, no? Or do they not support | the distributed version control approach and every developer | has to submit their changes to the current version of the | database? | JoshTriplett wrote: | Seems like an intended design feature. That doesn't mean you | have to _keep_ all those old versions in every copy of the | repository; you could always fetch only versions you need, for | instance. | lpointal wrote: | Unison is already the name of a bidirectional files | synchronization software (AFAIR developped in OCaml). | | https://www.cis.upenn.edu/~bcpierce/unison/ | pvg wrote: | Previously: | | https://news.ycombinator.com/item?id=22009912 | | https://news.ycombinator.com/item?id=9512955 ___________________________________________________________________ (page generated 2021-06-27 23:00 UTC)