SkillAgentSearch skills...

Py2hs

A complementary resource that helps python programmers to learn Haskell

Install / Use

/learn @cffls/Py2hs
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

py2hs

Introduction

I find it an interesting practice to learn and reinforce a new programming language by comparing it with a familiar language, and adding the distinct features and concepts from the new language on top of my existing knowledge system. This is a reference guide I created when I was learning Haskell.

This project is inspired by py2rs.

This is:

  • a complementary resource that helps python programmers to learn Haskell as a new language
  • a place for beginners to learn and reinforce some of the core concepts in Haskell

This is not:

  • an one-stop-shop that teaches everything about Haskell
  • an encyclopedia that aims at covering all similarities and differences between Python and Haskell

Table of contents

General

| Definition | Python | Haskell | | -------------------------------------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------- | | Programming paradigm | Imperative | Purely functional | | Type System | Dynamically typed | Statically typed | | First appeared | 1989 | 1990 | | File extensions | .py, .pyw, .pyc | .hs, .lhs | | Programming guidelines | PEP8 | Haskell programming guidelines |

Getting started with Haskell

Official Haskell documentation provides plenty of tutorials and books to start with.

https://www.haskell.org/documentation/

Code format

All examples are either code snippet or interactions with python shell or Haskell GHCi. Python shell examples starts with >>> , and Haskell GHCi examples starts with λ> . Please note that all Python codes are written in Python 3.


Functions

Basic function syntax

Python

>>> def multiply(x, y):
...     return x * y
...
>>> multiply(2, 3)
6

Haskell

λ> multiply x y = x * y
λ> multiply 2 3
6

Flow control

Python

def bigger(x, y):
    if x > y:
        return x
    else:
        return y

Haskell

bigger x y = if x > y
             then x
             else y

Explicit typing

Python

def bigger(x: int, y: int) -> int:
    if x > y:
        return x
    else:
        return y

Haskell

Approach 1: explicitly specify all inputs and the output
bigger :: Int -> Int -> Int
bigger x y = if x > y
             then x
             else y
Approach 2: set a type constraint Integral for a
bigger :: (Integral a) => a -> a -> a 
bigger x y = if x > y
             then x
             else y

Lambda function

Python

>>> list(map(lambda x: x + 1, [1, 2, 3]))
[2, 3, 4]

Haskell

λ> map (\x -> x + 1) [1, 2, 3]
[2,3,4]

Comments

Python

# This is a python comment
'''
Python
multiline
comment
'''

Haskell

-- This is a haskell comment
{-
haskell
multiline
comment
-}

List

List creation

Python

>>> [1, 2, 3, 4]
[1, 2, 3, 4]
>>> list(range(1, 5)) # Use range
[1, 2, 3, 4]

Haskell

Syntax sugar:

λ> [1, 2, 3, 4]
[1,2,3,4]
λ> [1..4] -- Use range
[1,2,3,4]

Without syntax sugar:

λ> 1:2:3:4:[]
[1,2,3,4]

List concatenation

Python

>>> [1, 2] + [3, 4]
[1, 2, 3, 4]

Haskell

λ> [1, 2] ++ [3, 4]
[1,2,3,4]

Adding element to list

Append

Python

>>> my_list = [1, 2, 3, 4]
>>> my_list.append(5)
>>> my_list
[1, 2, 3, 4, 5]

Haskell

λ> [1, 2, 3, 4] ++ [5]
[1,2,3,4,5]
Prepend

Python

>>> my_list = [1, 2, 3, 4]
>>> my_list.insert(0, 5)
>>> my_list
[5, 1, 2, 3, 4]

Haskell

λ> 5:[1, 2, 3, 4]
[5,1,2,3,4]

Indexing

Python

>>> my_list = [1, 2, 3, 4]
>>> my_list[2]
3

Haskell

λ> let myList = [1, 2, 3, 4]
λ> myList !! 2
3

List comprehension

Python

>>> [i * 2 for i in range(1, 11)]
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

Haskell

λ> [x*2 | x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]
List comprehension with conditions

Python

>>> [i * 2 for i in range(1, 11) if i % 2 == 0]
[4, 8, 12, 16, 20]

Haskell

λ> [x*2 | x <- [1..10], even x]
[4,8,12,16,20]
More complex list comprehension

Python

>>> [i * j for i in range(1, 5) for j in range(1, 5)]
[1, 2, 3, 4, 2, 4, 6, 8, 3, 6, 9, 12, 4, 8, 12, 16]

>>> [[a, b, c] for c in range(1, 11)
...     for b in range(1, c+1)
...     for a in range(1, b+1) if a**2 + b**2 == c**2]
[[3, 4, 5], [6, 8, 10]]

Haskell

λ> [ x*y | x <- [1..4], y <- [1..4]]
[1,2,3,4,2,4,6,8,3,6,9,12,4,8,12,16]

λ> [ [a,b,c] | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]
[[3,4,5],[6,8,10]]

Zipping

Python

>>> list(zip(range(1,5), range(2,6)))
[(1, 2), (2, 3), (3, 4), (4, 5)]

>>> list(zip(range(1, 5), [8, 9, 10]))
[(1, 8), (2, 9), (3, 10)]

Haskell

λ> zip [1..4] [2..5]
[(1,2),(2,3),(3,4),(4,5)]

λ> zip [1..4] [8,9,10]
[(1,8),(2,9),(3,10)]

Class -> data

In Python, and most OOP languages, Classes provide a means that bundles two things:

  • data
  • functions that retrieve, modify, or manipulate the data

Let's start with defining a class in Python.

from dataclasses import dataclass

@dataclass
class Square:
    side: float

    def area(self):
        return self.side ** 2
>>> my_square = Square(5)
>>> my_square.area()
25
>>> my_square.side = 6
>>> my_square.area()
36

Class Square bundles a float number, side, and a function, area, which calculates the area of the square. Attribute side could be directly updated. The functions bundled with the class are stateful, because the outputs of the same function could be different depending on the state of the class. Being stateful is like holding a double-edge sword. On one hand, you can make a class very flexible and adaptive to new changes. On the other hand, however, the behavior of functions could be non-deterministic with respect to their inputs, making them unpredictable, and therefore, difficult to debug.

Now let's see a way to define a Square and a function that calculates any Square's area in Haskell.

data is a keyword in Haskell. It defines a type of data structure, similar to Class in Python.

data Square = Square Float -- Defining "Square" as a data structure that holds a Float

area :: Square -> Float -- Declaring function "area" as a function that takes a "Square" and returns a Float
area (Square side) = side ^ 2 -- Implement the area function
λ> let mySquare = Square 5
λ> area mySquare
25.0

What we saw above, is a type (Square) that holds a float, and a function (area) that operates on this specific type, while not being bundled to the data structure itself. The separation of data and function, is one of the most important characteristics of Haskell. What's good about the separation of data and function? Stateless! Instead of depending on some "internal" state, functions are deterministic and predictable purely from their inputs.


Interface -> Type class

Area does not only apply to square, but all types of shapes. Let's start with defining an interface, area, in Python.

from abc import ABC, abstractmethod

class Shape(ABC):

    @abstractmethod
    def area(self):
        pass

Then we define a couple of shapes that implements area.

import math
from dataclasses import dataclass

@dataclass
class Square(Shape):
    side: float

    def area(self):
        return self.side ** 2

@dataclass
class Rectangle(Shape):
  
View on GitHub
GitHub Stars43
CategoryEducation
Updated8mo ago
Forks6

Languages

Python

Security Score

87/100

Audited on Aug 7, 2025

No findings