In which scenarios should I consider a functional programming languages better suited to do a given task? Besides the so recently popular multicore problem of parallel programming.
Anything that involves creating sequence of derived data elements using a number of transformation steps.
Essentially, the "spreadsheet problem". You have some initial data and set of row-by-row calculations to apply to that data.
Our production applications do a number of statistical summaries of data; this is all best approached functionally.
One common thing we do is a match-merge between three monstrous data sets. Similar to a SQL join, but not as generalized. This is followed by a number of calculations of derived data. This is all just functional transformations.
The application is written in Python, but is written in a functional style using generator functions and immutable named tuples. It's a composition of lower-level functions.
Here's a concrete example of a functional composition.
for line in ( l.split(":") for l in ( l.strip() for l in someFile ) ):
print line[0], line[3]
This is one way that functional programming influences languages like Python.
Sometimes this kind of thing gets written as:
cleaned = ( l.strip() for l in someFile )
split = ( l.split(":") for l in cleaned )
for line in split:
print line[0], line[3]
If I decided to switch to a functional programming language which do you consider are the biggest pitfalls that I will face? (Besides the paradigm change and the difficulty to evaluate performance due to lazy evaluation).
Immutable objects is the toughest hurdle.
Often you'll wind up calculating values that create new objects instead of updating existing objects. The idea that it's a mutable attribute of an object is a hard mental habit to break.
A derived property or method function is a better approach. Stateful objects are a hard habit to break.
With so many functional programming languages out there, how would you choose the one the better suit your needs?
It doesn't matter at first. Pick any language to learn. Once you know something, you're in a position consider picking another to better suit your needs.
I've read up on Haskell just to understand the things Python lacks.
An idiomatic Scala/LWJGL implementation of Space Invaders wouldn’t look that much like a Haskell/OpenGL implementation. Writing a Haskell implementation might be a better exercise in my opinion. But if you want to stick with Scala, here are some ideas for how to write it in functional style.
Try to use immutable objects only. You could have a Game
object which holds a Player
, a Set[Invader]
(be sure to use immutable.Set
), etc. Give Player
an update(state: Game): Player
(it could also take depressedKeys: Set[Int]
, etc), and give the other classes similar methods.
For randomness, scala.util.Random
isn’t immutable like Haskell’s System.Random
, but you could make your own immmutable generator. This one is inefficient but it demonstrates the idea.
case class ImmutablePRNG(val seed: Long) extends Immutable {
lazy val nextLong: (Long, ImmutableRNG) =
(seed, ImmutablePRNG(new Random(seed).nextLong()))
...
}
For keyboard/mouse input and rendering, there’s no way around calling impure functions. They’re impure in Haskell too, they’re just encapsulated in IO
etc. so that your actual function objects are technically pure (they don’t read or write state themselves, they describe routines which do, and the runtime system executes those routines).
Just don’t put I/O code in your immutable objects like Game
, Player
and Invader
. You can give Player
a render
method, but it should look like
render(state: Game, buffer: Image): Image
Unfortunately this doesn’t fit well with LWJGL since it’s so state-based, but you could build your own abstractions on top of it. You could have an ImmutableCanvas
class that holds an AWT Canvas
, and its blit
(and other methods) could clone the underlying Canvas
, pass it to Display.setParent
, then perform the rendering and return the new Canvas
(in your immutable wrapper).
Update: Here is some Java code showing how I would go about this. (I would have written almost the same code in Scala, except that an immutable set is built-in and a few for-each loops could be replaced with maps or folds.) I made a player which moves around and fires bullets, but I didn’t add enemies since the code was getting long already. I made just about everything copy-on-write – I think this is the most important concept.
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import static java.awt.event.KeyEvent.*;
// An immutable wrapper around a Set. Doesn't implement Set or Collection
// because that would require quite a bit of code.
class ImmutableSet<T> implements Iterable<T> {
final Set<T> backingSet;
// Construct an empty set.
ImmutableSet() {
backingSet = new HashSet<T>();
}
// Copy constructor.
ImmutableSet(ImmutableSet<T> src) {
backingSet = new HashSet<T>(src.backingSet);
}
// Return a new set with an element added.
ImmutableSet<T> plus(T elem) {
ImmutableSet<T> copy = new ImmutableSet<T>(this);
copy.backingSet.add(elem);
return copy;
}
// Return a new set with an element removed.
ImmutableSet<T> minus(T elem) {
ImmutableSet<T> copy = new ImmutableSet<T>(this);
copy.backingSet.remove(elem);
return copy;
}
boolean contains(T elem) {
return backingSet.contains(elem);
}
@Override public Iterator<T> iterator() {
return backingSet.iterator();
}
}
// An immutable, copy-on-write wrapper around BufferedImage.
class ImmutableImage {
final BufferedImage backingImage;
// Construct a blank image.
ImmutableImage(int w, int h) {
backingImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
}
// Copy constructor.
ImmutableImage(ImmutableImage src) {
backingImage = new BufferedImage(
src.backingImage.getColorModel(),
src.backingImage.copyData(null),
false, null);
}
// Clear the image.
ImmutableImage clear(Color c) {
ImmutableImage copy = new ImmutableImage(this);
Graphics g = copy.backingImage.getGraphics();
g.setColor(c);
g.fillRect(0, 0, backingImage.getWidth(), backingImage.getHeight());
return copy;
}
// Draw a filled circle.
ImmutableImage fillCircle(int x, int y, int r, Color c) {
ImmutableImage copy = new ImmutableImage(this);
Graphics g = copy.backingImage.getGraphics();
g.setColor(c);
g.fillOval(x - r, y - r, r * 2, r * 2);
return copy;
}
}
// An immutable, copy-on-write object describing the player.
class Player {
final int x, y;
final int ticksUntilFire;
Player(int x, int y, int ticksUntilFire) {
this.x = x;
this.y = y;
this.ticksUntilFire = ticksUntilFire;
}
// Construct a player at the starting position, ready to fire.
Player() {
this(SpaceInvaders.W / 2, SpaceInvaders.H - 50, 0);
}
// Update the game state (repeatedly called for each game tick).
GameState update(GameState currentState) {
// Update the player's position based on which keys are down.
int newX = x;
if (currentState.keyboard.isDown(VK_LEFT) || currentState.keyboard.isDown(VK_A))
newX -= 2;
if (currentState.keyboard.isDown(VK_RIGHT) || currentState.keyboard.isDown(VK_D))
newX += 2;
// Update the time until the player can fire.
int newTicksUntilFire = ticksUntilFire;
if (newTicksUntilFire > 0)
--newTicksUntilFire;
// Replace the old player with an updated player.
Player newPlayer = new Player(newX, y, newTicksUntilFire);
return currentState.setPlayer(newPlayer);
}
// Update the game state in response to a key press.
GameState keyPressed(GameState currentState, int key) {
if (key == VK_SPACE && ticksUntilFire == 0) {
// Fire a bullet.
Bullet b = new Bullet(x, y);
ImmutableSet<Bullet> newBullets = currentState.bullets.plus(b);
currentState = currentState.setBullets(newBullets);
// Make the player wait 25 ticks before firing again.
currentState = currentState.setPlayer(new Player(x, y, 25));
}
return currentState;
}
ImmutableImage render(ImmutableImage img) {
return img.fillCircle(x, y, 20, Color.RED);
}
}
// An immutable, copy-on-write object describing a bullet.
class Bullet {
final int x, y;
static final int radius = 5;
Bullet(int x, int y) {
this.x = x;
this.y = y;
}
// Update the game state (repeatedly called for each game tick).
GameState update(GameState currentState) {
ImmutableSet<Bullet> bullets = currentState.bullets;
bullets = bullets.minus(this);
if (y + radius >= 0)
// Add a copy of the bullet which has moved up the screen slightly.
bullets = bullets.plus(new Bullet(x, y - 5));
return currentState.setBullets(bullets);
}
ImmutableImage render(ImmutableImage img) {
return img.fillCircle(x, y, radius, Color.BLACK);
}
}
// An immutable, copy-on-write snapshot of the keyboard state at some time.
class KeyboardState {
final ImmutableSet<Integer> depressedKeys;
KeyboardState(ImmutableSet<Integer> depressedKeys) {
this.depressedKeys = depressedKeys;
}
KeyboardState() {
this(new ImmutableSet<Integer>());
}
GameState keyPressed(GameState currentState, int key) {
return currentState.setKeyboard(new KeyboardState(depressedKeys.plus(key)));
}
GameState keyReleased(GameState currentState, int key) {
return currentState.setKeyboard(new KeyboardState(depressedKeys.minus(key)));
}
boolean isDown(int key) {
return depressedKeys.contains(key);
}
}
// An immutable, copy-on-write description of the entire game state.
class GameState {
final Player player;
final ImmutableSet<Bullet> bullets;
final KeyboardState keyboard;
GameState(Player player, ImmutableSet<Bullet> bullets, KeyboardState keyboard) {
this.player = player;
this.bullets = bullets;
this.keyboard = keyboard;
}
GameState() {
this(new Player(), new ImmutableSet<Bullet>(), new KeyboardState());
}
GameState setPlayer(Player newPlayer) {
return new GameState(newPlayer, bullets, keyboard);
}
GameState setBullets(ImmutableSet<Bullet> newBullets) {
return new GameState(player, newBullets, keyboard);
}
GameState setKeyboard(KeyboardState newKeyboard) {
return new GameState(player, bullets, newKeyboard);
}
// Update the game state (repeatedly called for each game tick).
GameState update() {
GameState current = this;
current = current.player.update(current);
for (Bullet b : current.bullets)
current = b.update(current);
return current;
}
// Update the game state in response to a key press.
GameState keyPressed(int key) {
GameState current = this;
current = keyboard.keyPressed(current, key);
current = player.keyPressed(current, key);
return current;
}
// Update the game state in response to a key release.
GameState keyReleased(int key) {
GameState current = this;
current = keyboard.keyReleased(current, key);
return current;
}
ImmutableImage render() {
ImmutableImage img = new ImmutableImage(SpaceInvaders.W, SpaceInvaders.H);
img = img.clear(Color.BLUE);
img = player.render(img);
for (Bullet b : bullets)
img = b.render(img);
return img;
}
}
public class SpaceInvaders {
static final int W = 640, H = 480;
static GameState currentState = new GameState();
public static void main(String[] _) {
JFrame frame = new JFrame() {{
setSize(W, H);
setTitle("Space Invaders");
setContentPane(new JPanel() {
@Override public void paintComponent(Graphics g) {
BufferedImage img = SpaceInvaders.currentState.render().backingImage;
((Graphics2D) g).drawRenderedImage(img, new AffineTransform());
}
});
addKeyListener(new KeyAdapter() {
@Override public void keyPressed(KeyEvent e) {
currentState = currentState.keyPressed(e.getKeyCode());
}
@Override public void keyReleased(KeyEvent e) {
currentState = currentState.keyReleased(e.getKeyCode());
}
});
setLocationByPlatform(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}};
for (;;) {
currentState = currentState.update();
frame.repaint();
try {
Thread.sleep(20);
} catch (InterruptedException e) {}
}
}
}
Best Answer
functional languages are inspired by lambda calculus. In this field, parentheses are not used for function application.
Readability is in the eye of the beholder. You are not used to reading it. It is a bit like mathematical operators. If you understand the associativity, you only need a few parens to clarify the structure of your expression. Often you don't need them.
Currying is also a good reason to use this convention. In haskell, you can define the following:
With parens, you could use two convention:
f(5, 6)
(not curried) orf(5)(6)
(curried). The haskell syntax helps to get used to the currying concept. You can still use a non-curried version, but it is more painful to use it with combinatorsNotice how the second version forces you to register x as a variable, and that the subexpression is a function which takes an integer and adds 5 to it? The curried version is much lighter, but also considered by many as more readable.
Haskell programs makes extensive use of partial application and combinators as a mean of defining and composing abstractions, so this is not a toy example. A good function interface will be one where the order of parameters provides a friendly curried usage.
Another point: a function without parameters should be called with
f()
. In haskell, since you only manipulate immutable lazy evaluated values, you just writef
, and consider it as a value which will need to perform some computations when needed. Since its evaluation won't have any side effect, it makes no sense to have a different notation for the parameterless function and its returned value.There are also other conventions for function application: