Hacker News new | past | comments | ask | show | jobs | submit login
Why we write elementary apps in Vala (2014) (elementary.io)
185 points by auraham on Sept 10, 2022 | hide | past | favorite | 75 comments



A similar language that was created to make creating GUIs more pleasant is Jakt [1] for SerenityOS which is Impressive to see how fast they've been able to develop a pleasant modern native language, went from 0 to rust-based then self-hosting compiler inc. VS Code static analysis & intelli-sense in weeks.

Like most of SerenityOS you can follow much of its development is on YouTube [2]. A good video series that captures the language ergonomics is in the "Jaktness Monster" series which goes through creating a NES Emulator from scratch [3].

[1] https://github.com/SerenityOS/jakt

[2] https://youtu.be/RXp2T_iAhWc

[3] https://youtu.be/47svsnd7HIQ


Jakt looks pretty interesting. Would “rust without the productivity sapping affine types / lifetimes / borrow checker, but with zig’s take on comptime” be a fair summary or am i doing an injustice?


For me it looks like a more pleasant, productive and safer native language than C++ with emphasis on readability and safety. It's definitely inspired by other languages with similar goals but I wouldn't consider it defined by them.

One of its Co Creators @jntrnr was on both the Rust core language and TypeScript language teams which was how they were able to build the first rust based compiler + VSCode static analysis so quickly. Once the language was capable enough they rewrote the compiler in Jakt where it's now self hosting.

Excited to track its trajectory given how fast they were able to develop it, which already has a nice development experience in VSCode.

[1] https://twitter.com/jntrnr


Jakt looks very much like Swift. Objects are reference counted, structs are always value types, all function arguments require labels, no semicolons.. it's clear that Kling has worked for Apple.

I like the language! It is so much more readable than Rust. I might switch from Rust to Jakt once version 1.0 has been published.


Funny, because I feel that affine types make me much more productive in Rust :)

Comptime would be nice to have, though!


So they made Swift and slapped a new name to it


Exactly my thought. Looks like a poor man's swift (poor because many features from Swift are still only in planning).

The one difference is that jakt is transpiled to C++ (IMHO it's a disadvantage, but could be useful).


Obviously Swift is going to have more features given Lattner started first developing it in 2010, but it's built on flawed foundation given many years after release valid syntax would kill both the compiler and bring down Xcode as well.

Having experienced compiler engineers from the start is important for how well designed the language is syntactically and how strong its foundation is which helps with correctness and development velocity. Swift is always going to be the primary choice for Mac/iOS Apps but it sees relatively little usage elsewhere, either way it's going to be interesting to track Jakt's progress given how well designed it is and how fast they've been able to develop it.


>was created to make creating GUIs more pleasant

Checking through the repo and videos, I don't see how this is the case. It seems a pleasant language but there isn't something GUI specific. That said the last one seems to have an issue frequently attributed to Rust, specifically requiring much effort to wrap an "unsafe" library for usage on "safe" language.


In his videos Andreas mentions the motivation for creating Jakt was wanting a safe productive language for developing SereneityOS and its GUI Apps for the long term future as he didn't believe the trajectory of C++ was going to be suitable and found other languages unsuitable for adoption in SerenityOS so he teamed up with JT to create a new one without C++'s unsafe footguns, initially transpiling to C++ so it's easily integrated into SerenityOS build system and able to access their existing C++ libraries.

They're still actively developing inheritance support which I believe is the last remaining feature before they can start creating SerenityOS's (OOP) GUI Apps with it [1].

[1] https://youtu.be/47svsnd7HIQ


Any idea what kind of effort it would take to get Jakt working on another OS?


It already can, as it currently just transpiles to C++ it just needs clang to build [1].

Even though it doesn't require SerenityOS, even SerenityOS GUI apps can be ported to run on Linux, Andreas already has ported its Ladybird Browser that wraps SerenityOS LibWeb engine in a QT App [2] to Linux, his last video shows "Abstracting the event loop so Ladybird can be fast & responsive on Linux" [3]

[1] https://github.com/SerenityOS/jakt#usage

[2] https://github.com/SerenityOS/ladybird

[3] https://youtu.be/S8lXroxngYo


They're making it as a transpiler to their dialect of C++. On other OS it can also work because they have the Serenity Browser engine running on Linux. So probably take some parts of the build chain there and off you go.


GTK invented its own bespoke Object Oriented Programming system for C, called GObject: https://docs.gtk.org/gobject/

Naturally, it's very annoying to use this C-based OOP API in any other language, especially languages that have their own incompatible OOP type system like Java or C++.

One of the biggest reasons to use Vala is that it's OO system is GObject, making interacting with GTK and GLib code significantly more painless.


GObject is a real tragedy of the NIH syndrome in open source.

In the late 1990s there already was Objective-C. It already had open source compiler support, a battle-tested runtime, a free software desktop environment, and a corporate sponsor that became the world’s most valuable corporation.

Instead of using what was already there, the Linux GUI community extracted a poor shadow of Obj-C out of the GIMP image processing program (itself a poor shadow of a commercial market leader).


GIMP development started in 1995. The GNU Objective C frontend (the one that has stayed in use) started in 1993.

I would hardly consider the GIMP developers (who became the early developers of GTK) to be rash in their option not to adopt a language that had only been available for a couple of years in the open source world.

And all these years later, at a time when Apple has essentially abandoned Objective C, GTK and Glib continue to provide bindings for dozens of languages to create cross-platform GUI applications, and even if many of us don't agree with the direction(s) it has taken, GTK itself has continued to evolve, whereas UI libraries that came with Objective C have become frozen in time.

So in short, not really a tragedy at all.


Apple has not abandoned Objective C. It still has first class support on all their platforms, and the vast majority of their OS frameworks and applications are developed in ObjC. Swift is growing as an internal tool, but it’s nowhere near even 50% of the 1st party code on a mac or iphone.

Improvements have slowed on ObjC, as Apple’s focus switched to swift - ObjC 2.0 in 2014 was the last major change.


Also the most popular iOS third-party apps are all Obj-C[++].

The C++ interop is very important for companies like Meta. They have no incentive to rewrite anything in Swift.

If you look at minutes spent by end users on the iOS platform, I’m guessing it’s at least 95% inside Obj-C apps today.


Which is exactly why Apple showed the following at WWDC 2022, just to make it clear that the time for Objective-C is done.

https://developer.apple.com/videos/play/wwdc2022/102/

> The Objective-C language, AppKit & UIKit frameworks, and Interface Builder have empowered generations of developers. These technologies were built for each other, and will continue to serve us well for a long time to come, but over time new abstractions become necessary. For a while now, you've seen us hard at work defining the next generation of integrated language, frameworks, and tools: Swift, SwiftUI, and Xcode Previews.

> Tight integration in a development platform like this requires that all three pieces be designed and evolved together, both driving and driven by one another. Swift result builders were inspired by SwiftUI's compositional structure. SwiftUI's declarative views were enabled by Swift value types. And Xcode Previews was specifically designed for, and enabled by, both. Now, the result is the best development platform that we have ever built. And this year, Swift, SwiftUI, and Xcode all have fantastic updates that take this vision further, and make it even easier for you to build great apps for all of our platforms. And it all starts with Swift.


Nah. I’ll believe it when I see it.

Only when Apple comes out with a flagship app written in Swift (I would require SwiftUI too, but I don’t want to be that cruel…) will I entertain the idea that ObjC can be replaced. If they were eating their own dogfood internally, they should be aware that it is nowhere near ready for AAA app development.


watchOS comes to mind.

On Ventura several systems apps have been rewritten, including the preferences panel.


Those are not exactly the flagship apps I was talking about.


A complete platform used by millions of people, and apparently has saved several lives as per this week's announcement, doesn't count as flagship, got it.


It's like saying Tizen is flagship because it's used in millions of Samsung TVs.


> I would hardly consider the GIMP developers (who became the early developers of GTK) to be rash in their option not to adopt a language that had only been available for a couple of years in the open source world.

I think you misunderstood the criticism. They didn't say GIMP used the wrong model when it started, they said the greater community was wrong for copying GIMP later on.

> And all these years later, at a time when Apple has essentially abandoned Objective C, GTK and Glib continue to provide bindings for dozens of languages to create cross-platform GUI applications, and even if many of us don't agree with the direction(s) it has taken, GTK itself has continued to evolve, whereas UI libraries that came with Objective C have become frozen in time.

The suggestion is that Objective C should have been used instead of GObject. That doesn't mean having to use any particular libraries. And the same work to bind and evolve could have been poured into any reasonably solid library.


They did not copy GIMP, they literally extracted the widget code out of GIMP (and with it the object model) and made it into an independent toolkit. At the time there was no decent free toolkit at all, people were trying to reimplement Motif (Lesstif) or Qt (Harmony) but GIMP's was the first free software toolkit for X11.

Then over time it was extended beyond widgets, and the tooling around it grew to focus on exporting introspection information for language bindings.


My guess is that you can't point to a single example of a library written in ObjC that has any other language bindings, and if you can, it is barely used by anyone. The GTK community always made the idea of language bindings a central part of their development ethos.


> GIMP development started in 1995. The GNU Objective C frontend (the one that has stayed in use) started in 1993. I would hardly consider the GIMP developers (who became the early developers of GTK) to be rash in their option not to adopt a language that had only been available for a couple of years in the open source world.

Early in 1997, Guillaume Laurent and I spent some time looking into Objective C as a possible language for the "next version" of the Rosegarden sequencer on Unix/X11. At the time Rosegarden was a C application[1] using Athena widgets with my own little styling library, and we were sure there must be better options and were prepared to rewrite the UI entirely if we had to.

We ended up ruling it out for a few reasons, which I've just looked up in old email archives. I had some trouble getting the then-new gcc Objective C runtime working across Unix systems (we didn't only target Linux) and feared that we wouldn't be able to give our code to someone else and have them compile it easily. We couldn't find a working graphical toolkit: we thought GNUstep wasn't ready and also that it was probably going to be overkill. Other possibilities like NSXKit[2] weren't there either. Guillaume experimented with writing a toolkit himself but quickly ran into various obstacles that I don't have a record of.

(What is striking in hindsight is how hard it was to find reliable information to help us make a decision like this. There was documentation, and there were opinions, but there was very little of the form "we used this for our project and here's how it worked" that you would begin to see after blogging had become commonplace. We worked mostly on supposition and projection - install something, see if we could get it to build at all, spend a couple of hours with it and make guesses based on that, as well as on external info such as how much we knew about the developers that worked on it.)

So we switched to C++ instead. Considered Qt, argued about it, looked at LessTif, then GTK, then contributed a bit to gtkmm, then after a couple of years of nominally using gtkmm and getting nothing done, we switched to Qt after all and were happy. Happy-ish.

Even for people who really liked Objective C in 1997, it was pretty hard to make a case for. Maybe an open source company with staff and capital could have, but they barely existed.

[1] https://all-day-breakfast.com/cannam/x11-rosegarden/rosegard...

[2] http://www.hardcoreprocessing.com/home/anoq/Programming/GNUS...


How so?

You probably don't understand the context of mid-90s free and open software. Nothing is free, nothing open, relevant business models do not yet exist. Few things that are open barely work: linux takes hours to compile-tweak-recompile, all the tools are half-baked and semi-broken. You had to fix most things yourself.

Anyways, the only tools that do work reliably and have no license issues are coming from FSF: Emacs, gcc, unix utils, etc. Note that objC was not free, and the free version sucked most of the time. GCC did have world-class support for C though.

C was and still is a good choice for a core portable cross-language library. But UIs are probably the only programming context where OOP fits well. So people came with Gobject.

It all makes total sense, and that's why the whole thing survives to this very day.


> “C was and still is a good choice for a core portable cross-language library. But UIs are probably the only programming context where OOP fits well. So people came with Gobject.”

That’s my exact criticism: they came up with GObject instead of using the C-based solution that already existed, had open source compiler support, and was actively developed by Apple.

The GTK community made a huge mistake in trying to turn GIMP’s poor design into a generic GUI framework rather than collaborating with GNUstep on creating a next-generation framework.


Apple would never collaborated with GNUStep, first of all, when GNUStep started NeXT was still around.

Secondly, Steve Jobs wasn't an happy fellow about the whole story regarding GCC request to get back the frontend changes into upstream.

Gtk OOP is actually a clone of Motif OOP, which Gtk was cloning in first place.


They wouldn’t have needed Apple’s collaboration to build a new UI framework on Obj-C, just like nobody needed SCO’s permission to build Linux.

GTK’s two original sins were 1) cloning Motif in the first place, 2) doubling down on that design instead of throwing away GIMP as the prototype that it was.

They’ve been forever reliving the 1990s due to these bad choices.


Yet here they are, alive and kicking, while objc is gone, motif gone, etc. Long live gnome, long live gimp, long live gtk!


Gone? There are several orders of magnitude more users of Obj-C software than there are users of Gnome.


If you’re happy with this also-ran forever-4th-place-in-everything outcome, good for you. Long live the king of the desert island.

It could have been a lot more though.


> Gtk OOP is actually a clone of Motif OOP

And it has the characteristic GNU Lisp-envy-in-C (gnu_lisp_envy_in_c ()) flavour.

And the GNU Network Object Model Environment (GNOME) developers were (with Bonobo et al.) copying COM, if anything.

How did that even happen?


All ways of doing OOP in C will tend to resemble each other, if they are reasonably easy to use for a C programmer, without heaps of macros or code generation.

Anyway, COM is based on Microsoft RPC which is an implementation of DCE/RPC with extensions.

https://en.wikipedia.org/wiki/Microsoft_RPC


I don’t know. I mean, even classic Win32 has around two and a half object systems: windows+messages, standard COM, and IDispatch{,Ex}; and they are all rather different. (IDispatch and IDispatchEx, while a bit different from each other, are also neither particularly easy to use from C nor particularly good at their original purpose[1], so I’m counting them as half a system.)

Then there is Objective-C... OK, I don’t know that it’s that usable without a preprocessor, but there are Piumarta’s id[2] and Finch’s cobj[3], which are reasonably different from either of the Win32 systems.

(I don’t know to which degree DCE is relevant to COM in this comparison, given that in the intraprocess case all it inherits from there is the IDL and the non-exposed wire format when marshalling between apartments. Technically RPCRT4.DLL does contain obvious counterparts of DCE functions, but I haven’t yet found any Microsoft documentation suggesting you actually use those for anything.)

[1] https://ericlippert.com/2003/09/16/why-do-the-script-engines...

[2] https://piumarta.com/software/id-objmodel/

[3] https://dotat.at/@/2007-04-16-awash-in-a-c-of-objects.html


Miguel de Icaza has always been a fan from Microsoft technologies, that is how Bonobo happened.


But see also what happened with Qt and its license issues. That didn't happen with Objective-C, but nobody could predict the future at the time. There are some advantages to controlling your stack.

Also, pointing at GIMP's UI seems a little exaggerated, when the primary difficulty was keeping track of all the floating windows. GIMP seemed to have been designed in anticipation of a major advancement in window managers (not just tiling, but size-aware) that never materialized (the irregular packing problem is NP-Hard). Other Gtk apps never had this problem, and GIMP eventually switched to panes, thankfully.


1st, you're ignoring the fact that this c-based solution was not well-supported back then.

2nd, objective c is dead now. It was not a good long-term solution.

3rd, everybody and their cat knew C, while objc was a niche back then.

How is that a good choice for anything non-mac?!


> 2nd, objective c is dead now. It was not a good long-term solution.

How much is GObject used outside of GTK? Isn't that a hundred times deader?

The current number of Objective C users is not directly relevant to this comparison. The comparison pavlov made is to a hypothetical situation where GTK (or whatever you call it) was based on Objective C. If that had happened, GTK's object model would be much less 'dead'.

In that hypothetical world, it would still be sad to see Apple leave, but that world would have far more Objective C users than the current world has GObject users.


I've always wondered why GNUstep didn't work out; would've been a fantastic way of porting Mac OS apps, in theory at least. I guess that is what you're saying as well.


> In the late 1990s there already was Objective-C.

Objective-C is closer to the original, or at least early, Alan Kay and Smalltalk flavor of object oriented languages. In the late 1990s, we had already had 10 years of C++, so the C++ flavor "C with classes" type of object oriented programming was more familiar to most people.

I'd guess already then, the Smalltalk flavor of Objective-C felt a bit alien to many, who had grown familiar with the C++ flavor.


> GObject is a real tragedy of the NIH syndrome in open source.

It is, but I wouldn't look at Objective-C as the previous invention. X11 already hat the Toolkit Intrinsics (Xt), and it's not like they were unknown to the developers, the first version of Gimp having been written in Motif.

A new toolkit in the vein of Xaw/Motif/OLIT would've been a more immediate choice, but then again, no two persons seem to have the same preferences when it comes to object-orientation.

(I'm sitting in the proverbial glasshouse here, having switched my own "let's make some widgets for Qt 0.x" project to "let's reinvent X11 toolkits" in the mid to late 90s)


I've been writing C++ GUI apps with gtkmm for more than 22 years. I rarely notice GObject, other than in a few minor cases where the gtkmm wrapper didn't fully provide everything from the C level. In almost all those cases, the only reason I am still aware of them is because I use an ancient version of both gtk and gtkmm.

I would say that it is essentially completely painless to interact with GTK and Glib via gtkmm and glibmm.


> I've been writing C++ GUI apps with gtkmm for more than 22 years

How did you resist Qt?


When I started, Qt required a preprocessor (moc) for the build process. Gtkmm did not do, and use libsigc++. This seemed like a clear sign to me that it was technically superior.

22+ years later I would say that both Qt and Gtkmm have their own pros and cons. They have both run into pretty deep issues with integrating their own drawing model into the windows, macOS and X11 models. They both provide things that the other does not.

At this point, I don't see one as better than the other, and for a long established application like mine, the idea of switching is essentially unthinkable.


I did the same as PaulDavisThe1st; gtkmm is an excellent C++ interface for Gtk, such that I wonder why they even bothered to create Vala except for NIH syndrome. Qt had similar issues in the past: it wasn't pure C++, it was C++ "with extensions"; you had to use a custom compiler (moc), and you had to use Q-versions of everything the STL provides (QString, QVector, and so on).

Of course things were different more than a decade ago: C++11 did not exist yet or compiler support was lacking, and Gtk and Qt couldn't wait for it to catch up.


Yes you had to use qmake. 99% of the developers never even touched moc, it was baked into the build system and gave you things like signals and slots.

I remember doing this https://github.com/nurettin/trendline_analysis_qt/blob/maste... (Qt gave me graphicspathitems that I could do rubberband select on an accelerated qgraphicsview more than 15 years ago. I loved gtkmm and standard c++, but Qt was simply amazing. And nobody forced you to use Q*containers. I always found that argument a bit moot.

And it had none of these "problems" after 2012.


It's very pleasant to use in Python, though.

The object model is very close to C++ and Java, and I wonder if they could have made the memory layouts slightly different so that they were compatibile with C++, like in COM. But anyway gtkmm for C++ seems to work well.


That's a 2014 article by the way.

Did Elementary survive the fall out between its two cofounders? I haven't really followed.


Danielle has been posting substantial update blogs every month, so I'd at least tentatively say that the project seems to be moving forward.


It's a little bit sad, because I wish the game theory didn't work this way, and because I'm someone who tends to stay attached to the same project, but I almost wish smart people like her and anyone else working on ElementaryOS would move on to projects that have a better chance of gaining a foothold in the marketplace. At this point, I think EOS is probably NGMI


Honestly, I think this is the sort of project that where if you manage to get 0.5% of the market share you're still going to make enough money to get by. The market is massive. A partnership with the right orginsation to make this their operating system and it would be able to survive and allow the developers to carry on doing what they want to do.


Not a fan of bala itself, but I think this approach generates a filter for Elementary like apps, to have a strong commitment and standard practices that translate in an amazing UX and look and feel


ElementaryOS had an app store in which your code had to be open source and they still were going to take 30% of the revenue. As an indie app developer initially interested, that really turned me off.


Hey I just wanted to respond to this comment real quick. This platform fee covers the cost of payment processing and helps pay for the server infrastructure. AppCenter in itself is not a profitable venture at this time and it struggles to break even. Reducing this fee would guarantee that it runs at a loss, which might be feasible to consider in the future but for right now there’s not such a wide margin in the budget to run services that lose money


I actually don't have big problem with the 30%, especially for a small operation, but I do have a problem with both the 30% AND the ideologically-driven requirement that my software be open source. You just didn't provide a compelling alternative to other platforms. Your platform actually demands more of me, the developer, than Apple does, amazingly.


I don’t really want to wade into the Rust vs other languages thing, but I would be curious for Elementary’s take on why Vala over Rust (gtk-rs seems to see widespread use and all).

Anyone know if they’ve noted it somewhere? Is it just the investment in Vala and their custom widget library that set the direction?


Elementary is heavily dependent on the GTK ecosystem, which has its own OOP system implemented in C (for example like GObject). It is really hard to map GTK's OOP semantics to Rust. People have tried things like https://github.com/gtk-rs/gtk3-rs, but it requires heavy macro usage and doesn't feel like ergonomic Rust. On the other hand Vala is directly built upon the GObject system on the first place, so it's more fitting for GTK-specific development.


Yeah, this is a problem in a couple modern languages. For some time I tried to make a binding for Nim and encountered the same problem.

How do you make it so you can call "Widget" methods on a "Button"? Without proper subclassing, you have to either create a lot of duplicate functions, or the user has to explicitly cast up. Nim has implicit converters, so I tried defining conversions from all types to their base classes (Button -> Widget). But this made compilation time insanely slow.

Modern languages make this about "favor composition over inheritance" and say it helps you avoid monstrous Java-esqe class hierarchies. But it is really about subtyping and the Liskov substitution principle. You have a heterogeneous list of different "Widgets", how easily can you treat them as one kind, even if they need different behavior in detail?

Nim had experimental support for generics co-/contravariance. So you could say a "IF Button struct is a subclass of Widget struct THEN a GObjectPointer[Button] can be used in all cases where a GObjectPointer[Widget] can be used" (covariance). This was a unique feature and would solve the problem without implementation inheritance, but unfortunately it didn't work properly and I think it has been removed.


Nim actually does support inheritance though, so you could have just used that with methods. Though I can understand if you wanted to avoid inheritance.


That's true. I think my problem was that I was trying to be to clever and trying to make something like SmartPtr[TButton] have the same inheritance like ptr TButton. If I would have "wrapped" everything in native Nim classes it would have been fine. OTOH, I think there is no equivalent to an interface in Nim, so that would not work in that case.

I'm not saying Nim is deficient, in fact it is one of the most expressive and productive languages I know. But there are subtle difference between object models in different languages that make GUI programming hard, and I think that is one reason many people don't use modern languages but stay on C++/C# or throw there hands up in the air and just use Electron...


Yeah, tbh I think lack of interfaces is one of the biggest misses in Nim.


Until barely a couple of years ago, nobody wrote GUI applications in Rust anyway.

GTK's OOP semantics have been mapped very successfully to, among other languages, C++, Python, Perl and Go. It's unfortunate perhaps that the Rust bindings do not feel idiomatic for Rust programmers, but that's hardly fatal or unexpected.


I think it's just that Rust doesn't have inheritance/subclassing as a feature. As a result you have to do all sorts of mental gymnastics with impls and macros to fit Rust interface on GTK, while with other languages subclassing just "works".

I also think this is why Rust has lagged heavily on the GUI development front. No matter how you claim component or ECS-based architectures are better and faster, sometimes the only thing you really need might be a good ol' inheritance-based scenegraph... You can definitely do UI with a component-based object model (ex. Unity game engine), but the whole process seems a bit more... convoluted.


We have moved as an industry from "subclassing works great for GUIs" to "let's use it for absolutely everything else" through to "subclassing is a terrible idea, what were we thinking of?", whereas the first of those ideas is actually still sound.


Well, for a start, that blog post was written one year before Rust 1.0 was released.


A counterpoint: Vala hasn't done a 1.0 release yet.

But still, back at that time (2014) there was far less research done on how Rust library authors can ergonomic bind to C/C++ libraries that have their own object system. Nowadays people have figured out some techniques and we can actually use GTK and Qt from Rust, albeit in an awkward manner (lots and lots of macros).


It seems the benefits of vala were so large that not having a proper version one was not a problem.


Vala had made up its mind for many years on what it looks like and feels like. Older Rust version had green threads and IIRC even a garbage collector, it was absolutely not the same language as Rust 1.0.


Ha! You're totally right. I read this on mobile and I don't think the title had (2014) in it when I did, completely overlooked it.


I wonder whether this choice ended up leading to Elementary OS being the least stable Ubuntu-based I’ve ever attempted to use. I really wanted to make it work but neither Luna nor Jólnir were actually stable enough to daily drive, as much as I liked the looks of the DE.


  >Elementary OS – Why we write elementary apps in Vala (2014)
Fixed the title for you.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: