10 ms·
Cool to "see" F# in action. Like the union types, but not so much the list operations; seems more natural to: [1;2;3;4] filter isEven sum vs. List.filter is
by virtualwhys 13y ago
Cool to "see" F# in action.
Like the union types, but not so much the list operations; seems more natural to:
[1;2;3;4] filter isEven sum
vs.
List.filter isEven [1;2;3;4] |> List.sum
in Scala it's:
List(1,2,3,4) filter isEven sum
Of course I'm not familiar with F# so don't know all of the WIN within (Type Providers, for example, are very impressive, would love to see that on the Scala side of the fence one day).
- throwaway344 13y agoIn F#, most pipelined operations are expressed using the pipeline operator all the way. [1; 2; 3; 4] |> List.filter isEven |> List.sum That way you maintain the same operator order as you do in Scala, while maintaining some consistency with currying. In addition, you can add other functions really simply while still maintaining the order. [1; 2; 3; 4] |> List.filter isEven |> List.sum |> printfn "%i"
- virtualwhys 13y agoInteresting, in Scala you do: list filter isEven foreach println Which is, IMO, quite elegant, but then again I'm used to Scala and not yet at all familiar with F# and the reasoning behind the syntax.
- anonymoushn 13y agoList(1,2,3,4) filter isEven sum println error: type mismatch; found : Unit required: Numeric[?] List(1,2,3,4) filter isEven sum println ^
- virtualwhys 13y agoI didn't write what you wrote ;-) foreach takes a collection and applies some function (in this case println). I've actually had a use case for what you're trying do, so created an extension method on Any, which means you can then do, on Any-thing: (list filter isEven sum).echo I also hate typing "println". "echo" I can bang out instantly, and can do so on any expression, before or after its definition.
- ponzao 13y ago`sum` does not accept a function as a parameter, the original example had an `foreach` instead of `sum`. list filter isEven foreach println As a side note omitting that many commas and parenthesis is very rare at least based on the Scala projects I've worked on. The above example is equal to the one below: list.filter(isEven).foreach(println)
- virtualwhys 13y agoGood points, I tend to mix it up, rarely going parens-free style, but also not parens by default either. If you wanted to be really explicit you could do: list.filter(x=> isEven(x)).foreach(x=> println(x)) yuck ;-)
- ponzao 13y agoThat example is not quite equivalent, because now you are not passing `isEven` or `println` as arguments, you are passing anonymous functions that call `isEven` and `println`.
- latkin 13y agoThe most common way to format code like that in F# would be something like let evenSum = [1;2;3;4] |> List.filter isEven |> List.Sum Using |> is a win for two reasons - code flows more naturally, and the type inferencing works better. e.g. List.Map (fun d -> d.Hour) [DateTime.Now] does not compile, but [DateTime.Now] |> List.map (fun d -> d.Hour) does.
- thinkpad20 13y agoThat seems odd. It seems that `arg |> func` would simply be sugar for `func arg` (or a function that accomplished this). Why are they not equivalent?
- throwaway344 13y ago|> is a low priority operator, which forces evaluation to occur first on the left. [1; 2; 3] |> List.filter (fun a -> a < 3) |> List.sum is the same as List.sum (List.filter (fun a -> a < 3) [1; 2; 3]) arg |> func is the same as func (arg)
- thinkpad20 13y agoYeah, that's exactly what I thought. It doesn't explain (or I don't see it yet) why the first example he gave wouldn't compile, but the second would.
- latkin 13y agoF# type inference runs left -> right, top -> bottom. In the first snippet, the lambda is encountered first, and the compiler has no knowledge of what the type of "d" is, so it can't assume d has a ".Hour" member. In the second snippet, the compiler has already seen the input list and knows it contains DateTimes, so when it encounters the lambda, "d" is known to be a DateTime and everything is groovy.
- thinkpad20 13y agoAh, I see, thanks for that. So it's not running Hindley Milner (or at least, not unless it's quite heavily modified). That's not surprising, I guess, since it takes no small amount of effort to get HM to work in an OO/imperative setting.
- thinkpad20 13y agoI guess it's all a matter of what you're used to, because coming from ML-family languages like F# and Haskell, the first thing you wrote doesn't really make any sense to me. In the ML family like F# is, you put the function (e.g. "filter isEven") before the argument ([1;2;3;4]).
- virtualwhys 13y agoRight, which is why ML languages are a bit baffling for me, the ordering seems totally off ;-) Just getting started with Haskell and am having several, shall we say, WTF moments.
- bunderbunder 13y agoI've got little experience with Scala, but judging from your sample it looks like what's happening is that Scala follows more Java-y idioms for how code is structured. So in List(1,2,3,4) filter isEven sum it looks like filter and sum are instance methods on the List class, and I'm guessing isEven is a predicate that's being passed as an argument to the filter method. F# leans closer to its functional roots in this respect, so it's more idiomatic to keep object-oriented constructs at arm's reach in most your code. The language has full support for OOP, it's just that you're not expected to trot it out except when you're writing public interfaces that are meant to be consumed by code that might be written in C# or VB.NET. So the List class mostly sticks to static methods in its public interface because that approach fits better with traditional functional idioms. That gets you as far as something like this: List.sum (List.filter isEven [1;2;3;4]) The next step is the "pipeline" operator, which is defined as let inline (|>) x f = f x So it's just letting you swap a function and its argument, which facilitates reorganizing the code so that the functions are listed in the order in which they execute. That's what gets you to the example you give - or better yet: [1;2;3;4] |> List.filter isEven |> List.sum which I think captures some of the natural expression that you were talking about while still sticking with functional idioms instead of object-oriented ones.
- jdmichal 13y ago> So the List class mostly sticks to static methods in its public interface because that approach fits better with traditional functional idioms. And also fits perfectly with the C# and VB.NET extension method syntactic sugar, which was used extensively for LINQ. They're really just static methods in the .NET VM and bytecode.
- frowaway001 13y ago> that approach fits better with traditional functional idioms Just interested, what's the real value here? "Familiarity" alone seems to be a pretty poor reason. > They're really just static methods in the .NET VM and bytecode. Doesn't this mean that either your methods won't be dynamically dispatched or that you would need to rewrite your code if you ever change from e. g. List to Array? Looks like a bad compromise to me.
- Paradigma11 13y agoThe reason is that f# using the .net type system has no higher kinded generics. So you can abstract over t in List<t> but not over u in u<t>.