Smashing Swift
nomothetis.svbtle.com
nomothetis.svbtle.com
The primary vibe I'm getting from Swift is pragmatism. They had various red lines to follow: must be as fast as Objective C; must have a minimal to non-existent runtime (so AOT compilation all the way); mustn't have garbage collection, must interoperate with Objective C/normal C with ease; etc. This had led to a language which, despite being at version 0.1, has a fair few oddities and warts already. For example, the "almost but not quite immutable" arrays seem to be an artefact of absolutely wanting array performance to be equal to standard C. Typealiases vs parametric protocols seem to be a desire to have as much type information fixed as soon as possible.
It seems that rather than design a language where they might not have solutions for their red lines up front, they've designed a language where they can provide them. Rather than make theoretically "better" choices the compiler can't deliver perfectly in V1, but can in V4 or 5, they've gone for "yeah, we can almost certainly ship that". Given that this is essentially Apple's private language, I assume they'll be quite aggressive about deprecating features and moving people onto the "better" solutions (high-kinded types, richer immutability etc) when they can deliver them whilst meeting the core goals. It's an interesting approach - most other languages are happy to take a few years to get going, whereas Swift seems to want to go from 0 to 60 in 4 months. It reminds me most of C# 1.0, but with harder restrictions on what they've been told to deliver. At the moment, it's interesting, and a big leap from Objective C. By V3, it might be "excellent".
So did I! A few more related links if you're interested in this question:
http://schani.wordpress.com/2014/06/11/associated-types-cons...
http://www.artima.com/weblogs/viewpost.jsp?thread=270195 (this is about Scala, but Swift somewhat follows Scala with this design)
https://groups.google.com/forum/#!topic/swift-language/3Ptyd...
I also ran up against another annoying bug/oversight/v0.1ism : You can't inherit from a generic class without becoming generic yourself. So, even if you fully instantiate your parent's type information, you have to be generic as well! For example:
class Foo<T> { ... }
class Bar : Foo<String> { ... }
You'd expect Bar to be a non-generic type, but this throws a compiler error. You have to declare: class Foo<T> { ... }
class Bar<String> : Foo<String> { ... }
Which seems just odd. The best workaround I've found thus far is class Foo<T> { ... }
class BarClass<String> : Foo<String> { ... }
typealias Bar = BarClass<String>
Bleh. I've filed a radar against this one - it seems like an annoying oversight, even for this early version.The fact that an instance of A is assignment compatible with a location of type B does not imply that a class of type A is assignment compatible with a location of type class of B.
This is not the case for languages like C#, Java, C++ etc. (despite those languages not having class types); if a subclass does not define all the constructors of its ancestor class, the subclass is not a "drop-in replacement".
Delphi does have class value polymorphism that mirrors instance polymorphism, and it can be a source of confusion, not to mention type holes. For A inheriting from B, you can construct a value of type A using a constructor of B if you assign A to a location of type class of B; A's constructor won't run, and its assumptions and invariants won't necessarily hold. It's one of several problems that makes designing robust classes in Delphi awkward.
Indeed, calling it Bar<String> seems really weird to me. Usually, in a declaration, the thing inside the angle brackets is a the name of a parameter, which can assume various values at runtime:
class Foo<T>
But in the subclass, it's the name of a type: class Bar<String>
Essentially, one is a declaration, and the other is an expression. Like and l-value and an r-value. I don't think i've ever seen a language which allows both of those in the same place.`class Bar<String> : Foo<String>` is exactly the same as `class Bar<T> : Foo<T>`. The identifier `String` in declaration of `Bar` does not refer to built-in type String, it is a tag for any type.
In other words, you CAN have `Bar<Int>`, `Bar<Float>` and `Bar<everything else>` after using that declaration of `Bar`.
class NodeList extends List<Node> { ... }To me, the more important answers are deterministic destruction, and no GC pauses. All of Objective C is reference counted these days, with retain/releases inserted by the compiler (so all you have to do as a programmer is resolve cyclic references with weakness). Thus, you know exactly when an object will die, and have its dealloc method called. You're also sure you'll never end up in a situation where memory pressure causes system hitching due to GC. Given that a key platform for Objective C is iOS (low memory, "low" CPU), and that Apple's trademark tends to be fluid UI, avoiding these problems is really helpful.
As for memory pressure, reference counting does generally behave better, but in some situations you can end up doing much worse. If you manage to generate a lot of autoreleased objects (harder to do these days with ARC, but still possible) then you can end up getting your process terminated due to the memory pressure of should-be-dead-but-not-yet-freed objects.
I think that the C interop problems are the real killer. The other problems with garbage collection can potentially be solved, but as long as C is in the picture, you're doomed to a sort of halfway land where none of the good GC techniques are available to you.
For multithreading, I thought that would be fairly obvious. Once two or more threads hold ownership over a single object, you can no longer be sure which thread will perform the final decrement (at least in the general case) and so you don't know exactly when the object will be destroyed, or even which thread it will be destroyed on.
If, and this is a big if, Apple is actually interested in Swift in the long term, then they will open up the language to community development so that these sorts of things can be added by the community.
My suspicion is, though, that Swift will be improved by Apple alone and that it will be pragmatically discarded when the time is appropriate. Which doesn't mean it won't have a long life, just an undignified exit.
One of recent technologies that fall into this category is auto layout. (The performance of auto layout in iOS 7 for slightly more complex table view cells is pretty bad)
Apple main interests is always Apple, which makes sense because they are a for profit company. Just as Rails is always Basecamp. Apple technology have a fast paced development because Apple dog food their technology. WebKit in Safari, LLVM in Xcode. They invested huge in these area. That doesn't mean that they don't open up. both technologies I mentioned are open source and have huge impact to the community as a whole.
E.g. RubyMotion make use of LLVM to allows ruby to be compiled to both iOS and Android.
People like to think Apple is a closed company (as marketed by its competitors) but people who know the company well know that is far from true. They might not be the most open of company but they had open source technologies that are awesome in their own right. LLVM is just one of them. Open sourced and won the ACM award. That by itself is a achievement hard to beat.
I don't understand how a community project became "Apple's achievement."
Apple did not open source LLVM. LLVM was an open source project (since 2000), which Apple used and open sourced their contributions to on the way.
This sounds and awful lot like what C++11/14 would bring, with a few platform specific extensions... but then again, no vendor, and especially not Apple, likes the thought of working with a language that's really trying to be cross-platform and a language that "rewards smart programmers" ( http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Key... ), as opposed to being easy-to-teach or idiot-proof...
I know, <your-favorite-language-almost-nobody-uses> is nicer. But for me, if it has GC, it's a no go. I see Swift as something where I win when I can use it instead of writing C, not as something where I lose since the most of problems I solve by writing software I can't solve by writing in <some other language>.
Swift is impressive language for v0.1, very pragmatic in aspects I care about. So you see that there's chance it will probably be even more expressive in some other version. Nice. Even as it is, it's much nicer than any other options for the purposes for which it was designed.
I still claim there isn't at this moment any more innovative language that can provide that what Swift provides, observing any GUI platform you want to observe: meaning that level of support for the platform's native GUI with that performance and elegance.
Seems like that criteria would bias the choice of most innovative language quite heavily towards languages developed for/by Apple, no?
https://news.ycombinator.com/item?id=7898305
From the article:
"no, your experience with server-side programming does not adequately prepare you to “think small” and correctly reason about mobile performance."
> https://news.ycombinator.com/item?id=7898305
Red herring. aidanhs' reply was to someone who genuinely asked why garbage collection is problematic. The comments I'm talking about have not mentioned garbage collection, just questioning what innovative really is. I guess you then tried to infer that they thought that garbage collection vs. reference counting doesn't matter, i.e. isn't innovative? I find that to be an unfair inference. They might as well think that something like that has already been done/is being done, i.e. with Rust and how it relegates any automatic memory management to opt-in libraries.
So it seems to be a disagreement about what is really worthy of being called innovative, not any technical merit, as the comments that I'm talking about has not questioned that.
> From the article:
> "no, your experience with server-side programming does not adequately prepare you to “think small” and correctly reason about mobile performance."
Straw man, unless you somehow know that all of these people only have experience with server-side programming.
It's like you'd read about the first usable jet-powered car and then claim "but it's not innovative, there were jet powered planes already."
Don't pretend iOS' fluidness is some merit of Objective-C or Swift, because it is a merit of the operating system.
https://news.ycombinator.com/item?id=7898305
then complain to all the authors that measured all that they measured (a lot of links and diagrams there) instead of complaining to me. It will still be an attempt to confront all the real measurements with your anecdotal evidence of one, your, particular case, unless you manage to disprove all the claims listed there.
Especially if you manage to disprove this:
http://sealedabstract.com/wp-content/uploads/2013/05/Screen-...
You'll be celebrated as the biggest genius of our times.
You were talking about a language's capabilities of creating fast responsive UIs on limited hardware. There are no language features that contribute to that, it is the operating system and runtime that does that. Are these things well done on Apple systems? Yes, absolutely. But that has no relation whatsoever to the discussion whether Swift is an innovative language or not. Apple could have bolted Common Lisp, Fortran or Rust onto that runtime, and that would have no relevance at all to the discussion about the innovativity of those languages.
Windows Phone also has a good runtime despite that it runs various garbage collected languages. Fluid apps are created for it all the time for low memory systems in various languages. But that doesn't make Visual Basic an innovative language.
Unless there is an understanding of that there's no point in arguing further. The whole mentioned article explains, among other topics, exactly that.
How about naming one that actually now does the same with the native access to any GUI resource constrained platform? F# certainly can't avoid the graph quoted. The y axis is the slowdown as soon as there isn't enough memory. The point you yourself claim that you don't understand, so how can we discuss anything further? I actually wrote my comments assuming that the people who'd discuss would understand the issues I assumed to be well-known and thoroughly documented among other places in the Drew Crawford's article.
Mobile phones are generally memory constrained as compared to desktop computers, because memory takes space and power. In addition, their memory bandwidth is usually _extremely_ constrained as compared to desktop computers, making big copies, etc, expensive.
You may have considerable difficulty finding a language that anyone uses that is innovative, in that case. Most new programming language concepts are explored in research projects, and _then_ incorporated into languages.
Not innovative compared to what? Haskell? Unfinished betas like Rust? Some language 100 people use (plus 1-2 banks and a couple of universities)?
This is a language that will jump in the top ten of most used languages in a year or so, just because it's used in a hugely popular development platform. And it had to fulfil several things to achieve that.
>They could've hired some smart people from more software-oriented companies or something
Because Chris Lattner, the guy behind the infrastructure of tens of new languages and one of the most popular C/C++ compilers, is not smart enough right? Or the team that created Swift.
And which company would that be? Go, for one, is like a 1980 language compared to Swift. And C# had 14 years to evolve, and didn't have the same functionality constrains Swift has handicapped with at all.
Swift is a nice language for sure. It picks up nicest features of many other language and doesn't trade that off with many annoyancies (from what I've seen so far). It also appears like a very accessible language. It breaks with older languages where those older languages are bad (eg "==" vs "===") but mostly sticks to things that work and are familiar. I think these criteria are important for what I think is "good language design".
Swift is not in any reasonable sense an innovative language. There is really nothing in there that is new. Now you can get worked up about that, or just accept that innovativity is really something far less important (or perhaps even undesirable) when compared to "good design" for Swift. Especially given its unique position.
I agree, didn't intent to dismiss Rust per se -- I like Rust.
Just wanted to give a perspective. Swift is in beta (and buggy), but Rust (if the OP compared it to that), isn't even syntactically stable.
Based on what we know, Swift will be in a (pretty much) stable condition come Fall, for the OS X release.
Shouldn't that be something we would want?
(Is it because control over this allows us to do extra things?)
ARC is very little different for the programmer, but suffers less from these problems and is easier to integrate with C language code.
No reason for GC.
It boils down to there being a big performance penalty on garbage collection in memory-constrained environments.
protocol Functor {
typealias T
typealias FunctorResult
func map<P>(mappingFunction: T -> P) -> FunctorResult
}
extension Dictionary : Functor {
func map<P>(mappingFunction: ValueType -> P) -> Dictionary<KeyType, P> {
var newDict:Dictionary<KeyType, P> = [:]
for (key, value) in self {
newDict[key] = mappingFunction(value)
}
return newDict
}
}Put another way, you need to specify FunctorResult at implementation time, and the whole point is to only have to specify it at call time.
let d = [1: 1, 2: 2, 3: 3]
let intToInt = d.map { $0 + 1 }
var IntToString = d.map { String($0) }The more canonical example of a Functor is really the Optional. The mapping method for an optional looks like this:
func fmap(f: A -> B) -> Optional<B> {
switch self {
case Some(let a):
return Some(f(A))
case None:
return None
}
}
However, with your protocol, I can define the mapping function for the Optional as: func fmap(f: A -> B) -> B[] {
switch self {
case Some(let a):
return [f(A)]
case None:
return B[]()
}
}
This would pass the type-checker, but is not what a Functor does.The typical efficient implementation of a protocol (aka an interface) is a vtable - an array of pointers to functions. When you instantiate a generic in a statically compiled language, that usually clones the body of the generic thing (method or type) with references to the generic parameters replaced with the type arguments; and the new, cloned body is handed off to codegen.
But if you instantiate via a protocol reference, the compiler doesn't statically know the implementation of the thing you're trying to call - it can't see the body of code to clone. It's an indirect function call through a variable, and without a restricted language, analyzing it quickly runs into the halting problem.
With runtime code generation, the problem can be solved with sufficient magic - the runtime can rewrite as necessary. That's how .NET implements this.
Alternatively, if the body of each generic method is compiled using some form of dynamic dispatch - e.g. every operation on a value whose type is a generic parameter is done via an table of function pointers, and this table is passed into the generic method at the point of every call - then it can work, at some cost in speed. Haskell works like this, AIUI.
Java does it even more simply, with full-fat dynamic dispatch - all generic parameters turn into Object, and method bodies get runtime casts inserted as necessary.
The third problem a work around exists for: remove the default argument. If you want an empty init, provide one.
Sure it is buggy, but you can express these programs.
My initial guess is that a functor is just sort of like a function that casts or does something like return an interface type from a type that implements the interface or a base class. Is that right? I still don't understand why an array is a functor though, I'd imagine it would at least have to be a function?
Edit:
Actually I think he's talking about Haskell's version of an iterable, and it would then make sense to call an array a functor.
https://en.wikibooks.org/wiki/Haskell/The_Functor_class
Why is that called a functor instead of an iterable or enumerable though?
A Functor is a type t parameterized by some other type a, such that if you have a function from type a to type b, you can apply that function to a t of a and get a t of b.
Most commonly, a functor will be a container of values of type a, and the mapping consists of applying the function to each value, resulting in a container of values of type b.
Other common functors include a computation that returns type a (use composition to apply the function to the result to get a computation that returns type b), or a possibly null pointer to type a (map null to null, map pointer to a to pointer to b).
If you think of a function as being a way of converting from one kind of thing to another, then a functor is an object which has a number of things which you can convert to some other kind (a number which may be one or zero), using a method of your choice, and map is the name of the method you use to do that.
More please !
Also guys, please check this new submission.
https://news.ycombinator.com/item?id=7895901
Alice and Bob play a cryptographic tetris game. Some french phd bloke has written it.
It is a pattern, yes. Let me show you a pattern that I have observed in Hacker News and forums since the demise of USENET.
USENET had an elegant way of ignoring without hellbanning, which the negative karma has done to me now.
Would you please study the history of Stalinism ?
Let me summarise it for you.
Ideology -- pg.
Yeah, Marx is a good guy too.
It is made of dictators and the inner ring -- Admins and Karma users like you. These people have special "powers" of punishment.
Admins have far greater powers of purging and renaming things.
Then the party cadre -- people who submit and actually keep the things in motion. They are usually not aware of the "inner ring", users like you. They want to help the lay people.
In fact no one even knows who is in the inner ring. The inner ring is clueless of the Admins.
Then there are lay users like me, who lurk around and want to help but are generally punished.
If you think I am shitting you consider the rate of new users becoming high karma users in HN over the years.
You can use d3 and it will go the top of HN.
Your pedantry is just arbitrary. There is nothing really substantial in it. Rather than judging on the basis of content, you are judging me on the "words" I use.
Gimmick: An ingenious or novel device, scheme, or stratagem, especially one designed to attract attention or increase appeal.
Why thank you, it is novel :)
Please understand the democratic roots of the word "forum" and take it seriously.
"Functor" is a terrible name that only a mathematician could like.
(The function being passed in to map() is an alternate way to represent a very large map, in a mathematical sense.)
I think this is the case for functors, because I've yet to see a metaphor for them which isn't misleading or restrictive. "replaceEach", for instance, would confuse me. I like working with parser combinators, where parsers are functors. If I replaceEach a parser, what have I replaced? The results of the parse? The parser hasn't been run on an input yet. Maybe it never will.
If I replaceEach a promise, what am I replacing? The result of the promise? Maybe the promise will be forever blocked, or maybe I'll cancel it.
If I replaceEach a continuation, what am I replacing?
Unless some C++ people decide that “functor” is a wonderful name for a stateful function-like thing which somewhat resembles closures/lambdas but has nothing whatsoever to do with the functors from functional programming.
These fine distinctions don't belong in code meant for a general audience. Instead, don't try to generalize so much. It's not important (and in fact it's confusing) that replaceEach can be generalized to work with a parser. Parsers can have a different function with its own name and nobody needs to know that it's sort of the same concept.
(I'd probably have called the 'Map' class 'Dictionary', if that hadn't already been taken!)
I'd avoid 'replaceEach', because it sounds like it mutates the receiver. Maybe 'withEachReplaced'? Way clunky. In Smalltalk, this method is called 'collect', which is better than 'map', to my eyes, but still not all that obvious [1].
[1] Smalltalk has the names collect, inject, select, and reject for its map, foreach, filter, and complementary filter methods, because of a song: http://smalltalkzen.wordpress.com/2011/02/02/arlo-guthrie-an...
That blog post inspired a fun discussion in the Smalltalk community on what the semantics of #infect: and #neglect: should be!
interface Functor<A, R extends Functor<A, R>>
And then in the implementing class, bind R to the implementing class: class Option<A> implements Functor<A, Option<A>>
However, this doesn't let you write an actual functor. Try to write the map method on it: class Option<A> implements Functor<A, Option<A>> {
private final A value;
public Option(A value) { this.value = value; }
@Override
public <B> Option<A> map(Function<A, B> fn) {
// er, i want to return an Option<B>, not an Option<A>
}
}
The problem is that you don't want to return an instance of the type of the receiver, you want to return an instance of a similar type with a different type parameter (Option<B> rather than Option<A>).You would be absolutely fine if your functions were functions from values of some type to other values of that type (eg functions from integers to integers), because then A = B and you can get away with this. But there's no way to extend this hack to let you return an Option<B> from map.
The closest you can get in Java is probably this:
interface Functor<A, R extends Functor<?, R>>
class Option<A> implements Functor<A, Option<?>> {
private final A value;
public Option(A value) { this.value = value; }
@Override
public <B> Option<?> map(Function<A, B> fn) {
return new Option<B>(value != null ? fn.apply(value) : null);
}
}
There, you leave the element type of the returned functor undefined, as a wildcard, which lets you slip an Option<B> out as a return value.The problem with that is that it's useless. Given:
Option<String> x;
Function<String, Integer> fn;
Then mapping the function over the option gives you: Option<?> y = x.map(fn);
An Option<?>. Which is no use to man or beast, because it's lost the type information.What you really want to be able to write is:
class Option<A> implements Functor<A, Option<_>>
Where the underscore is borrowed from Scala to mean "nothing to see here, move along please", and leaves that reference to Option in its unbound form. And then have a way of binding it right in the definition of the map function. But that's higher-kinded types, and Java doesn't have that.If you were absolutely desperate to do this in Java, like if terrorists burst in and put a gun to your head and told you to do it, you could restore the type information by pebble-dashing the functor with some simple reflection:
interface Functor<A, R extends Functor<?, R>> {
<B> R map(Function<A, B> fn);
<B> Functor<B, R> as(Class<B> b);
}
class Option<A> implements Functor<A, Option<?>> {
// other stuff as above
@Override
public <B> Option<B> as(Class<B> b) {
return new Option<B>(b.cast(value));
}
}
Which, given the above definitions of x and fn, lets you write: Option<Integer> y = x.map(fn).as(Integer.class);
And even the same thing but typed as functors, to show you there's nothing up my sleeve: Functor<String, Option<?>> fx = x;
Functor<Integer, Option<?>> fy = fx.map(fn).as(Integer.class);
And as a separate function which contains no mention of the concrete functor class anywhere (although it does still need a type token for the result functor's parameter): private <A, B, F extends Functor<?, F>> Functor<B, F> applyAbstractly(Functor<A, F> fx, Function<A, B> fn, Class<B> b) {
return fx.map(fn).as(b);
}
But to be honest, all of this is a bit like trying to carve a fine wooden sculpture with a chainsaw. If you want to do this, don't use Java. If you want to use Java, don't do this. public interface Mappable<T extends Mappable<T>> {
T map();
}var a = ['a', 'b', 'c']
I'd like instead to have the index of their position in the alphabet. That's another array of the same length that would look like this:
var i = [0, 1, 2]
To each element in a, there corresponds an element i, at the same index. And the operation we use is the same for each element:
f('a') = 0
f('b') = 1
f('c') = 2
But we could also want an array that gives us the next letter in the alphabet, like so:
var n = ['b', 'c', 'd']
Here the function is:
g('a') = 'b'
g('b') = 'c'
g('c') = 'd'
But we're very much doing the same thing. We call it mapping. So Arrays in Swift have a map function defined that works like this:
var i2 = a.map(f)
We're passing the function to apply to each member, and we get an array back, of the same length, with the result of doing so.
A functor is a generalization of that concept. It takes a type parametrized by another type, say Box<V> and a function from the parametrized type to another type, say f:V -> W. Then, according to rules that are specific to Box, if you map f over Box<V>, you'll get a Box<W> out.
The key is that exactly how a Box<V> becomes a Box<W> is up to Box.
Why do you get a composition of functions by applying a functor to individual functions? It seems like a functor is just something you can map over. When a functor is mapped over the end result is another functor. Not a function nor a composition of functions.
A function is a Functor. ;)
If you have a function f and a function g, fmap f g = h, and h is a function. A more common way to write this is function composition of f and g, which can be written: f . g. The dot (.) is function composition. Which means that fmap f g = f . g, which means that fmap = (.).
> When a functor is mapped over the end result is another functor. Not a function nor a composition of functions.
But they was describing the case when the Functor in question is a function.
fmap (g . f) = (fmap g) . (fmap f)
and
fmap id = id
where (.) :: (b -> c) -> (a -> b) -> (a -> c) is the function composition operator and id :: a -> a the identity function. Those are the laws the last two sentences try to phrase in english. In mathematics a functor F between categories C and D maps objects in C to objects in D and any morphism f: X -> Y in C to a morphism F f : F X -> F Y in D, in such a way that for morphisms f : X -> Y and g : Y -> Z in C one has F (g . f) = F g . F f and F id_X = id_{F X}. So you see Haskell and math notation are almost identical, although you can express the laws only as compiler rules in Haskell.
Types which are Functors may have orders and may provide access to the elements, but the Functor interface does not provide those means. This allows you to instantitate more restrictive types. For instance (in Haskell notation)
data Pretend a = Pretend
is a data type with only one element (`Pretend`) that pretends to be a container. Consider the two types Array Int
Pretend Int
You can still consider Pretend to be a Functor (the mapping function is just a no-op) but it certainly isn't iterable.In Haskell, Iterable is called Foldable and effectively is the following interface
instance Foldable c where
toList :: c a -> [a]
but `Foldable` is used because typically instead of converting it to a list you want to fold over the elements fold :: Foldable c => (a -> b -> b) -> b -> c a -> bProbably because 1) it is inspired by category theory rather than some interface/signature from another programming language 2) "Iterable"/"Enumerable" might be misleading.
2): It might make sense for things like arrays, where you can think of the function (fmap) as enumerating/iterating over the array and then, for each value in the array, uses the supplied function on it. But Functors aren't just things that you can iterate over; functions themselves are functors, and function composition is fmap. But how does the iteration-intuition make sense in this case? Other functors are more a kind of "box" than a collection that can be iterated over (for example: the Option/Maybe type). Sure, you can say that you can implement fmap on Maybe by iterating over the data that it holds, namely the single data that it holds, but that doesn't really say much.
map abs [-1, 2, 3] => [1, 2, 3]
Arrays are the simplest example, but it looks like an iterable. However functors retain shape whereas iterables don't. For example if we had a tree (represented visually): oak = -1
/ \
2 3
If we used oak as an iterable, we would lose the structure of the tree: map abs iter(oak) => [1, 2, 3]
However if tree belongs to the functor typeclass (i.e. implements functor interface), then: map abs oak => 1
/ \
2 3
The alternatives to functors are:1. mutate the existing data structure
2. copy a new one and then mutate (two passes)
3. create a new one while mutating (one pass, increased complexity / bugs)
Wow. Such a simple sentence yet this is the first time I have read it, and it makes the concept much more clear than hundreds of articles before did. Thank you!
I wish all FP features would be explained so simply.
map id c = id c
(Mapping the identity function is the same as simply applying the identity function -- or with a little more category theory, the functor maps the identity function in the base category to the identity function in the functor's category) map f (map g c) = map (f . g) c
The second law is also simple -- mapping one function over the container then mapping another function is exactly the same as mapping the composition of the functions over the container. This one is the basis of stream fusion, a very important optimization, that allows you take two traversals of a container and turn them automatically into just one traversal.The preservation of structure follows from just these two laws and parametricity (The element type of the containers is generic and therefore unknown, this greatly restricts what operations are available on the elements of the container. You can't, for example, conjure up a new value of the element type to insert.). I strongly recommend trying to figure out how.
[0] http://www.stanford.edu/class/cs106l/course-reader/Ch13_Func...
I have absolutely no experience on implementing compilers, but for a layman these inconsistencies seems very odd.
The idea behind LLVM was to have the various compiler stages and tooling be re-usable and plugin like, unlike the monolithic design GCC had.
And Apple already uses the came "compiler-as-a-library" approach, even for Objective-C, to implement: the compiler, the syntax highlight, AST-based auto-completion, debugging and error fix suggestions and other stuff.
I'm pretty sure that the case with Swift is the same. It's just that it's not stable yet, so you can get crashes at various stages of all those pipelines.
More specifically, the idea behind LLVM was "life-long program compilation and optimization" in stages at translation time, link time, install time, and runtime. The natural way to realize this was modular components at different levels working around a good serializable IR.
If your goal is a traditional once-and-done AOT compiler, you might be forgiven for architecting something more heavily coupled and interdependent like GCC, whose IR was an afterthought (by 15 years.) LLVM's focus has shifted somewhat, but those original designs created a kind of serendipitous foundation.
Now, not supporting functors is odd, though.
Ωmega, maybe? Is that what we're expecting of the new "normal" already?
We should not be comparing the development of languages now to 20 years ago - there is an enormous amount we have learned, and these insights should be fundamental considerations when designing a new language. You can't just "tack" things onto an existing language and expect it to be elegant - tacking on leads to huge languages like C++.
And yes, we should expect it as "normal" for new languages, because it is what people have come to expect to have available - although some implementations leave a lot to be desired.
If we consider how C# implements Functors for example, we see that it requires special language support, where the compiler essentially pattern matches over your code, looking for a method named "Select", with a specific signature taking a generic type and a func. This implementation completely misses the point that Functors themselves, while useful, are not special - they are a specialization of a more general concept we have - typeclasses and higher-kinded polymorphism. C# also adds the ability to create Monads, Foldable, Comonads etc, using similar patterns - but you can't create your own typeclasses, and are left to the language designers to add a desired feature.
The decision to add them onto C# like this was made not without knowledge of this, but out of consideration of working with the existing language, which was designed without their consideration, hence, why they're a pretty ugly design compared to the simplicity of Haskell's implementation.
[1]:http://www.cs.tufts.edu/comp/150GIT/archive/mark-jones/fpca9... [2]:http://research.microsoft.com/en-us/um/people/simonpj/papers...
By the way, thank you for the timeline for Haskell. That's exactly what I was wanting to know.
It does come up a lot in threads about Rust in other communities :)
We've discussed how HKT could integrate into the system and I think we have a pretty good concept of how it would work. But I would caution that uniqueness and low-level memory management can often throw a wrench into the common use cases you might think of for HKT, and functional features in general.
AFAIK you can't do Functor in Rust yet either, and for the same reason.
These are obvious things to try, from the POV of environments like Scala. That's the wrong context from which to evaluate this language. Swift is a more modern language for iOS and is most usefully evaluated in that context. (Though it is also important to also know what Swift is not in language terms.)
I Agree with what Chris Granger said in https://twitter.com/ibdknox/status/473912605350719488.
I think Rust is a good example of developing a language in the open.
That said it's great to see Bret's ideas see more implementations.
Also - what good is Rust being developed in the open if it can't be used for production apps yet?
These rose-colored glasses are getting ridiculous.
It's possible, of course, that Apple will modify the grammar to make these use cases impossible. But I would be very surprised. I can't blame anyone for accusing me of rose-tinted glasses until we know for sure, though.
I think the point here is that if the compiler crashes, that implies that it is trying to do the thing, as opposed to saying "No, filthy programmer, I will not permit you to do that thing."
Is it because ObjectiveC is as shitty as it seems at first sight (for C like langs dev) ?
My hypothesis was: Objective-C is "not nice" (sorry, I'm not familiar with HN political correctness) therefore Swift feels like a true relief for iOS devs and thus this much traction.
To me the comparison that makes more sense is going from C to objc. Or alternatively, comparing objc and C++ (especially C++ as practiced in the 1990s, not the RAII or template patterns that emerged later).
Say you're looking at the landscape as it existed a while back, and you've decided to make language changes to C in order to add objects. On the one hand you have C++ as it existed then: a language that can't seem to figure itself out, that has a very complex syntax and lots of troubling situations you can walk into.
Then along comes objc. In contrast to C++, the language delta from C is tiny and easy to keep in your head. Especially before Apple started doing all this compiler-fu of recent years, it felt like just a few keywords bolted on, and most of the important additions seemed to be in the runtime library.
When I first saw objc it kind of clicked for me as the anti-C++, in a way that is truer to C and doesn't add a lot of complexity. At the time that seemed refreshing, though I think I see more good in C++ now that RAII and templates are more of a thing. (If only there existed a set of 2 people who could agree on what C++ features to use.)
I don't mind the syntax, but I don't think it's the nicest language out there feature wise.
Okay enugh bullshit and to the point: Swift is preview. It will get better and better fromday to day. It isn't there to mimic scala or lisp paradigms. It is there to get the job done and it will get the job done.
Well, aren't you wonderful? Why, you'll probably win the Nobel Prize for Protecting the Poor Oppressed Men!
Alternating he and she for an abstract person is quite common amongst people who don't use singular they.
For those wondering about the weird "betafication" thing, I recommend reading this fine blog: http://wehuntedthemammoth.com/
It's a trope of the self-anointed "men's rights" crowd, from people who are in a continual hysteria about how society is stopping them from being proper alpha males. Never quite getting that passive-aggressive anonymous-coward Internet message board comments are not how chimpanzee males work out their dominance issues.