Programming Page

Programming tips, theory, terminology, and examples (mostly in Python)

Patrick Reany

Before you take any advice from me about programming, I need you,
the reader, to know who I am: I am a self-styled Christian author and
writer of mathematics papers, mostly expository. Although I have
been programming on and off for four decades, I'm no expert on the
subject. At the moment, I'm trying to learn Python by engaging
myself in making small programs that are useful to me -- very
motivating that way. Besides that, I'm not above asking for ChatGPT
to assist me in this process, or in looking for similar programs on line.
So, if you are just a beginner in Python programming, maybe you can
learn something from what I place here.

When it comes to learning a new programming language, my motto
is: Learn to think small. Then, after mastering small programs,
think up a project that will demand that you go to the next level
of sophistication. Anyway, that's what I try to do.


ChatGPT provides a Python Program. ChatGPT wrote a Python
program for me to find the GCD of two numbers, using the Euclidean
algorithm. Included in this article is a comparison of various other
Python programs that do the same thing, but are coded differently.
Very instructive.

The following are the four Python programs referenced in the
above article. The first is the one provided by ChatGPT, the
others are available on line (references provided).

The ChatGPT version.
The developerpublish version.
The Geeks4Geeks version.
The StackOverflow version.

An interesting syntax in Python is revealed in the command
"a, b = b, a % b", which is found on line 4 of the program
provided by ChatGPT. What it means is this: Set the new value
of a to the old value of b, and then set the new value of b to the
value of the expression a % b, when using the old values for a and b.
BTW, "a % b" means the remainder when dividing a by b
longhand, sotospeak.


What is functional programming?

First off, functional programming is a philosophy on how to program
with the intension of writing code that has no side effects. Thus, it's
a set of constraints that help one reduce the chance of a programming
bug arising in one's software. As such, it can be implimented in most
high-level languages, though some languages may facilitate it better
than others.

There are three major doctrines of functional programming:

In a language that supports functional programming, functions
can be used almost anywhere a number or a literal can be used.
For instance, one function can return another function, reducing
the number of temporary variables and allowing for function
composition. To test the correctness of my intuitive understanding
of the advantages of functional programming, I asked ChatGPT
for some help so that I get my delivery to my readers correct.

Patrick: Comment on the correctness of the following claim, please:
In a language that supports functional programming, functions
can be used almost anywhere a number or a literal can be used.
For instance, one function can return another function,
reducing the number of temporary variables and allowing for
function composition.

ChatGPT:

The claim you provided is generally correct. In a language
that supports functional programming, functions are treated
as first-class citizens, meaning they can be used in a similar
manner as other values like numbers or literals. This enables
powerful programming techniques such as higher-order functions,
function composition, and function chaining.

Here are some key points that support the claim:

Functions as values: In functional programming, functions
can be assigned to variables, passed as arguments to other
functions, and returned as values from functions. This flexibility
allows functions to be used and manipulated just like any other
data type.

Returning functions: In functional programming, a function
can indeed return another function. This concept is known as
a higher-order function. It enables the creation of functions
that encapsulate behavior or customize the behavior of other
functions, leading to more modular and reusable code.

Reducing temporary variables: By using function composition
or chaining, you can eliminate the need for intermediate
variables. Instead of assigning the result of one function to
a temporary variable and then passing it to another function,
you can directly compose or chain the functions together. This
can lead to more concise and readable code.

Function composition: Functional programming languages often
provide mechanisms for composing functions together. Function
composition allows you to combine two or more functions to create
a new function. This technique is beneficial for creating complex
operations by breaking them down into smaller, reusable functions.

While the claim is generally true, it's important to note that
the extent to which functions can be used in different contexts
depends on the specific language and its implementation of
functional programming features. Some languages may have more
extensive support for functional programming paradigms than
others, so the exact capabilities and syntax may vary.

I asked BingChat the following question: Is there a close
relationship between functional programming and category theory?

BingChat replied: Yes, there is a close relationship between
functional programming and category theory. Category theory
is a mathematical discipline that concerns itself with how objects
map to other objects. In functional programming, morphisms
are interpreted as functions, but they can also be thought of as
well-defined behavior that's associated with data. Concepts like
Category, Functor, Monad, and others, which were originally
defined in Category Theory, have become pivotal for the
understanding of modern Functional Programming languages and
paradigms.
I also asked ChatGPT the same question:
Yes, there is a close relationship between functional programming
and category theory. Category theory provides a mathematical
framework for describing and analyzing the structure of mathematical
objects and their relationships. It abstracts away the specific details
of particular mathematical domains and focuses on the relationships
between objects and the mappings between them.

Functional programming, on the other hand, is a programming
paradigm that emphasizes the use of pure functions, immutability,
and the avoidance of mutable state and side effects. It treats
computation as the evaluation of mathematical functions and encourages
the use of higher-order functions and function composition.

Category theory and functional programming share several
fundamental concepts and principles, making them closely related:

Abstraction: Both category theory and functional programming
emphasize abstraction. Category theory provides a powerful
tool for abstracting mathematical structures and reasoning about
them at a higher level. Functional programming encourages the
use of abstractions such as higher-order functions, which allow
you to treat functions as first-class citizens.

Composition: Category theory focuses on compositionality--the
ability to combine objects and morphisms to form new ones. Similarly,
functional programming promotes function composition as a
fundamental operation. It allows you to build complex computations
by combining smaller functions together.

Types and Type Theory: Category theory deals with objects and
morphisms, while functional programming often employs a type
system to reason about program behavior and correctness. Type
theory provides a formal foundation for understanding and
verifying the properties of programs. Category theory can be seen
as a higher-level theory of types, where types become objects and
functions become morphisms.

Monads and Functors: Monads and functors are concepts from
category theory that have found extensive use in functional
programming. Monads provide a way to encapsulate computations
and handle effects, while functors allow the mapping of
functions over structured data types. Both concepts have been
instrumental in the development of functional programming
techniques and libraries.

Immutable Data: Functional programming promotes immutability,
where data structures are not modified in place but rather
transformed into new structures. Category theory also deals
with transformations and mappings, focusing on the relationships
between objects. The notion of immutability aligns well with
the categorical notion of morphisms.

Due to these shared concepts and principles, category theory
has influenced the design of functional programming languages
and the development of functional programming techniques. Many
functional programming languages, such as Haskell, incorporate
category theory concepts directly into their type systems and
standard libraries. Category theory provides a theoretical
foundation for reasoning about functional programs, and
functional programming techniques often leverage category
theory to create more robust and expressive software.

Patrick: Is it true, then, that Haskell is fit for both categorical
programming as well as functional programming?

ChatGPT replied:

Yes, that is correct. Haskell is a programming language that
is well-suited for both categorical programming and functional
programming. Haskell incorporates many concepts from category
theory into its design, making it a natural choice for working
with categorical abstractions. At the same time, Haskell is a
functional programming language that fully embraces the
principles and techniques of functional programming.

Here are some reasons why Haskell is considered suitable for
both categorical programming and functional programming:

1) Type System: Haskell has a powerful and expressive type system
that aligns well with category theory concepts. It supports
higher-kinded types, type classes, and parametric polymorphism,
which allow you to define and reason about abstract structures
in a categorical manner. Haskell's type system provides a strong
foundation for encoding and working with categorical abstractions.

2) Higher-Order Functions: Haskell treats functions as first-class
citizens and encourages the use of higher-order functions. This
aligns with the functional programming paradigm, where functions
can be passed as arguments, returned as results, and composed together.
Higher-order functions are essential for categorical programming
as well, as they enable the composition of morphisms.

3) Purely Functional: Haskell promotes the use of pure functions,
which do not have side effects and produce the same results for
the same inputs. This purity aligns with category theory's
emphasis on transformations without side effects. By avoiding
mutable state and side effects, Haskell allows for referential
transparency and makes programs easier to reason about and test.

4) Monads and Functors: Haskell has built-in support for monads
and functors, which are concepts derived from category theory.
Monads provide a way to encapsulate computations with effects,
while functors enable mapping functions over structured data types.
These abstractions are essential in both categorical programming
and functional programming and are widely used in Haskell code.

5) Laziness: Haskell is a lazy language, meaning that computations
are not evaluated until their results are actually needed. Laziness
allows for more efficient and concise code by deferring computations
until they are required. It also aligns with the notion of morphisms
in category theory, which can be seen as describing transformations
without specifying when or how they are executed.

Due to these factors, Haskell provides a strong foundation for
both categorical programming and functional programming.
It allows programmers to work with categorical abstractions
and reason about them using category theory concepts, while also
enjoying the benefits of functional programming such as purity,
higher-order functions, and strong static typing.


Later on, I returned to ChatGPT for a deeper explanation:

Could you expound on this in some detail, please?:

"Monads provide a way to encapsulate computations with effects,"
which you stated above. Are these 'effects' the operations that
some programs must do, which pure functions are not allowed to do?

ChatGPT replied:

Yes, that's correct. In the context of functional programming,
"effects" refer to operations that go beyond the pure computation
of a function and interact with the external world or modify the
program's state. Examples of effects include reading from or
writing to a file, making network requests, interacting with a
database, generating random numbers, or even printing to
the console.

Pure functions, in contrast, are functions that only depend on
their inputs and produce a result based solely on those inputs.
They do not have any observable side effects and consistently
return the same output for the same input. Pure functions are
easier to reason about, test, and compose because they have no
hidden dependencies or mutable state.

Monads provide a way to encapsulate and manage these effects
within the context of a pure functional program. They allow you
to introduce and sequence effects while maintaining referential
transparency and preserving the benefits of pure functions.

In Haskell, a monad is a type class that provides a set of
operations and laws for working with effectful computations.
The key operation in a monad is usually called "bind" or ">>=",
and it allows you to sequence computations that have effects.
By using monadic operations, you can compose functions that work
with effects, ensuring that the sequencing and combination of
effects is controlled and managed.

For example, let's consider the Maybe monad in Haskell. It
represents computations that may produce a value or fail,
encapsulating the effect of possible failure. The Maybe monad
provides a way to chain computations together, where each
computation depends on the result of the previous one. If
any computation in the chain fails, the subsequent computations
are short-circuited, and the failure is propagated.

Here's a simple example using the Maybe monad to perform a
series of computations that may fail:

haskell

divide :: Double -> Double -> Maybe Double
divide x 0 = Nothing
divide x y = Just (x / y)

compute :: Double -> Double -> Double -> Maybe Double
compute x y z = do
    a <- divide x y
    b <- divide a z
    return b

In the compute function, we use the do notation, which is
syntactic sugar for using the monadic operations >>= and return.
Here, divide represents a computation that may fail by returning
Nothing. By using the Maybe monad, we can chain these computations
together, and if any of them fail, the result of the entire computation
is Nothing.

Monads provide a general framework for encapsulating and
managing a wide range of effects in functional programming.
Other monads in Haskell, such as IO for performing input/output
operations or State for managing mutable state, allow you to
work with different types of effects while maintaining the benefits
of purity and referential transparency.

In summary, monads in functional programming provide a
mechanism to encapsulate computations with effects, allowing
you to work with effects in a controlled and composable manner
while preserving the purity and clarity of the rest of the program.


I had some confusion on exactly how a function can give inconsistent
outputs with the same input, so I asked ChatGPT for additional
explanation.

Patrick:

I'm trying to think of occasions in which a function will output
more than one response to a given input. The example that comes
to mind is when a function uses a global variable whose value may
change through the running of the program. Can you think of any
others?

ChatGPT

In functional programming, it is generally desirable for a function
to produce a unique output for a given input, which is known as
referential transparency. However, there are cases where a function
may appear to produce multiple responses for the same input due to
external factors or non-deterministic behavior. Here are a few
examples:

1) Non-deterministic Functions: Some functions may have
non-deterministic behavior, meaning that they can produce
different outputs for the same input. These functions typically
involve randomness or external factors that affect their results.
For instance, a function that generates random numbers based on
a seed value will produce different results each time it is called
with the same seed.

2) Time-Dependent Functions: Functions that rely on the current
time or other time-dependent factors can produce different outputs
for the same input at different points in time. For example, a
function that retrieves the current time and performs some
computation based on it will produce different results as time
progresses.

3) Functions with Hidden State: Functions that depend on hidden state,
such as global variables or mutable state outside the function's scope,
can produce different outputs for the same input if the hidden state
changes. This is often considered a side effect and goes against the
principles of functional programming. Functions that rely on hidden
state can make it harder to reason about the behavior of a program and
can lead to unexpected results.

It's worth noting that functional programming encourages the use
of pure functions that don't rely on external state and produce
a unique output for each input. However, there are cases where
non-deterministic or time-dependent behavior is necessary or
desirable. In such situations, it is important to carefully manage
and control the effects to maintain the overall functional nature
of the program. Techniques like encapsulating effects in monads or
using functional reactive programming (FRP) can help manage such
scenarios while preserving the benefits of functional programming.