Course:CPSC312-2023/Character Avatar Maker

From UBC Wiki

Character Avatar Maker

Authors: Al Yu, Sarah Gu

example of command line app interactions in our program

What is the problem?

Our goal is to create a command line application that creates a humanoid avatar image based on a series of inputs provided by the user. The program will prompt the user for features of the character's appearance and display possible inputs for the various questions asked.

a sample output generated by our program

What is the something extra?

Using a folder of custom assets, the program will merge them together according to the user input to create a .png file of the avatar in an output folder. These can be viewed through a file explorer.

What did we learn from doing this?

Due to Haskell being a pure functional programming language that has no side effects, debugging the program went flawlessly. It was astonishingly simple to determine potential errors through the Haskell compiler being able to type-check all functions and their outputs at compile time. Additionally, it made reasoning through the code a lot easier, as we could directly assume the result of certain functions being used in an overarching function. For instance, we encountered an issue where merged pixels were being displayed without their alpha value (ie. level of transparency) being taken into account. This led to image assets having a very sharp border around the edges. Knowing the aforementioned, it was quick for us to determine which function was resulting in the error - the only thing left we had to do was determine the correctness of our algorithm that produced the value of a given colour channel. Another bonus was Haskell's method of lazy evaluation - it allowed us to abstract away a significant portion of our code to reduce code duplication, especially during IO operations.

In spite of these benefits, we still encountered difficulties. Namely, Haskell's design of IOs/Monads due to the elimination of side effects meant it was impossible for us to make changes to global variables. This resulted in many "function containers" that had to constantly pass down arguments to later functions, which hampered the readability of our code. Another major issue was the lack of object classes in Haskell. In object-oriented programming, it's fairly simple to add new fields to a class. However, with Haskell, it was incredibly difficult for us to determine the most optimal set of type classes and types to use to represent an avatar. We eventually decided on a few constants that would help restrict our code to the specified parts in an Avatar typeclass; unfortunately, adding a new feature to the Avatar typeclass meant updating several different typeclasses and types, depending on the level of complexity of the part. Similarly, complex parts were difficult to abstract due to other parts being very simple. The lack of objects to help simplify the Avatar and its parts really made it difficult for us to abstract all of the functions.

In short, Haskell is an excellent language when building function-based programs with little need for proper objects - which is no surprise, considering Haskell is a functional programming language. Aside from this, it's also simple to bugfix and refactor, which actually made learning Haskell and its libraries easier. However, it does start to struggle in terms of abstraction and complicated data structures due to Haskell's avoidance of side effects. If we were to create another Haskell project in the future, we'd most likely develop projects with a focus on calculations, such as a game or a calculator.

Links to code, etc.

https://github.com/hmqyu/haskell-avatar-maker