Representative Functional Languages and Their Features
Bài đăng này đã không được cập nhật trong 9 năm
Functional Languages
Any programming language supports and/or encourages functional programming is called a functional language.
What is functional programming?
A functional program is simply an expression, and executing the program means evaluating the expression.
In imperative programming, execute a function may result in different values in different time. It is because imperative programming relies on modifying the state through a sequence of commands.
In contrast, execute a function f(x)
twice in functional code will give the same output in any given time. Since there is no assignment in functional programming, therefore, no variables to assign and no state is changed (no side effect).
Representative Functional Languages
There are more than 20 functional languages you can find here
In this article we will explore an purely functional language (Haskell) and a mix of imperative and functional language (Ruby)
Haskell
Haskell is a purely functional language, which means that in general, functions in Haskell do not have side effects.
Haskell has some handy features:
- Pattern matching
- Lazy evaluation
- List comprehension
Pattern Matching
Haskell has a cool way to construct function by using Pattern Matching
. Pattern Matching are ordered set of patterns which data should conform and will be deconstructed accordingly.
In the following code, the data passed to the function will checked against the patterns from top to bottom, and if the pattern is matched, its body will be used.
lottery :: (Integral a) => a -> String
lottery 9 = "Jack pot!"
lottery x = "Sorry, good luck next time!"
So here if you call lottery(5)
you will get the message Sorry, good luck next time!
because 5
is not matched to 9
Therefore, calling lottery(9)
will give out the message Jack pot!
because of matched pattern.
Note: If you define a function without catch-all pattern it will crash some time.
sayMyName :: Char -> String
sayMyName 'a' = "Alfred"
sayMyName 'b' = "Bob"
sayMyName 'c' = "Chris"
So when you try to call sayMyName 'y'
you will get error Non-exhaustive patterns
because of the unexpected input.
By defining functions using separate patterns with their own bodies, programmer can write really tidy and readable code.
Lazy evaluation
Lazy evaluation is a method to evaluate a Haskell program. It means that expressions are not evaluated when they are bound to variables, but their evaluation is deferred until their results are needed by other computations. In consequence, arguments are not evaluated before they are passed to a function, but only when their values are actually used.
In simple term, an argument, inspite of being passed to a function, will not be evaluated until its result is needed in the function. More interestingly, a shared expression will not be evaluated more than once because its result is shared between places in which it is used.
For more of Haskell lazy evaluation check this link
List Comprehension
As you know that in Mathematics there is something called set comprehension
which is quite similar to list comprehension
.
A set comprehension looks like this:
A set of doubles of natural numbers which are less than or equal to 10.
This set can be expressed by using list comprehension
[x*2 | x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]
Similar to set comprehension
in Mathematics, isn't it?
Let's add condition to the list: tripple of the number is greater than or equal to 12
[x*2 | x <- [1..10], x*3 >= 12]
[8,10,12,14,16,18,20]
More examples of list comprehension can be found here
Ruby
Support Functional programming
Ruby is an imperative language but we can still apply functional principles. It's the developer's choice to assure immutability of data.
For example:
Bad:
balances = {"LeBlanc" => 16000}
balances["Yasuo"] = 20000
balances #=> {"LeBlanc"=>16000, "Yasuo"=>20000}
Good:
balances = {"LeBlanc" => 16000}
all_balances = balances.merge("Yasuo" => 20000) #=> {"LeBlanc"=>16000, "Yasuo"=>20000}
Don't update, create a new one if you're following functional principles.
Ruby has built-in methods with functional approach and its destructive version. Look at the example of select
and select!
numbers = [1, 2, 3 ,4]
numbers.select { |num| num > 3 }
numbers #=> [1, 2, 3, 4]
numbers.select! { |num| num > 3 }
numbers #=> [4]
Input should not be updated after executing a function. Again it's the programmer's choice to apply functional approach or not.
Everything is object
Either a piece of information is a string or an integer, that piece of information is an object. An object has its own properties and actions. In Ruby context, properties are instance variables and actions are methods.
For example: an integer can use times
method
10.times { print "I am tired of writing this line 10 times \n" }
Observing the ancestors of an integer shows that an integer is a Fixnum
and inherited from Object
class
10.class
#=> Fixnum
10.class.ancestors
#=> [Fixnum, Integer, Numeric, Comparable, Object, Kernel, BasicObject]
The same goes for a string
"Hello World".class.ancestors
#=> [String, Comparable, Object, Kernel, BasicObject]
Block
Closure can be attached to any method. A closure in this context is a block and what is a block
?
A block is simply code that is inside do
and end
. There are two ways to write a block
do
andend
code block{ }
inline code block
We can take an example of Array#select
:
[1, 2, 6, 9, 69].select { |number| number > 5 }
#=> [6, 9, 69]
#or
[1, 2, 6, 9, 69].select do |number|
number > 5
end
#=> [6, 9, 69]
|number|
in this example is a block parameter and its value is each number in the array.
Blocks are inspired by functional languages. Matz said, “in Ruby closures, I wanted to respect the Lisp culture.”
Implicit return
In ruby, return
is optional unless you want explicit return. The value of the last expression will be the return value of the method.
numbers = [1, 2, 3, 4]
def collect_square numbers
squares = []
numbers.each do |num|
squares << num * num
end
squares
end #=> not so good
#Using functional approach and implicit return
def collect_square numbers
numbers.map { |num| num * num }
end
# Both examples will return [1, 4, 9 ,16]
One application of this feature is method chaining
Let's say you have a Car
and wants to perform multiple actions
class Car
attr_accessor: position
def initialize options = {}
@position = 0
end
def move_forward distance
self.position += distance
end
def move_backward distance
self.position -= distance
end
end
You want something like this: Car.new.move_forward(100).move_backward(50)
Simply return self
at the end of move_forward
and move_backward
will do the magic
class Car
...
def move_forward distance
self.position += distance
self
end
def move_backward distance
self.position -= distance
self
end
end
Everything is open
You can extend any classes and modules in Ruby, including built-in classes and mdoules. This makes everything open in Ruby.
For example you want to have something like 6.plus(11).minus(4)
module Fixnum
def plus x
self.+ x
end
def minus x
self.- x
end
end
6.plus(11).minus(4)
#=> 13
References
http://www.rubytips.org/2008/04/07/10-unique-ruby-language-features/ https://code.google.com/p/tokland/wiki/RubyFunctionalProgramming http://learnyouahaskell.com/syntax-in-functions
All rights reserved