Haskell Database Integration: Persistent and Esqueleto Essentials Quiz

Explore the basics of integrating Haskell with databases using Persistent and Esqueleto. This quiz covers schema definitions, query construction, migration handling, and key features to help you confidently work with relational databases in Haskell applications.

  1. Defining a Persistent Entity

    Which of the following best represents how you define a database entity in Persistent using Haskell syntax?

    1. You use a special quasi-quoted block with entity, fields, and types listed.
    2. You call persistentEntity function with table and fields as parameters.
    3. You declare each field in a record and pass it to defineEntity.
    4. You import a Template Haskell module for each table and use entityGenerator.

    Explanation: Persistent uses a quasi-quoted syntax where you list entities and their fields along with types in a distinct block. The other options suggest patterns or functions that do not exist in Persistent's standard setup: there’s no persistentEntity function or entityGenerator; defineEntity is also not used this way. Only the quasi-quoted block method aligns with Persistent’s entity definitions.

  2. Basic Persistent Query Structure

    In Persistent, what does the selectList function typically return when called in a database action?

    1. A list of entity record types wrapped in an Entity.
    2. A string containing the SQL statement.
    3. An IO action that writes results to a file.
    4. A Maybe value containing one entity or Nothing.

    Explanation: The selectList function returns a list of database entities, each wrapped in the Entity type, which includes the key and the field values. It does not return SQL strings, a Maybe value, or perform IO file actions directly. The other choices either refer to incorrect return types or functionalities not provided by selectList.

  3. Executing Esqueleto Joins

    How would you perform an SQL inner join between two tables using Esqueleto’s EDSL in Haskell?

    1. By using the innerJoin function and specifying the condition.
    2. By using joinTables with ON and entity references.
    3. By listing both tables in the FROM clause without conditions.
    4. By calling selectAll with two entity names separated by a comma.

    Explanation: Esqueleto’s EDSL offers the innerJoin function, where you specify the join and its condition much like SQL's INNER JOIN. The other options misrepresent Esqueleto’s syntax or functions—selectAll and joinTables do not exist, and listing tables in FROM without conditions does not specify a join in Esqueleto.

  4. Persistent Migration Purpose

    What is the main purpose of running runMigration or equivalent in a Persistent-based Haskell application?

    1. To ensure the actual database schema matches the defined entities.
    2. To generate foreign key relationships in memory only.
    3. To delete all tables before starting the server.
    4. To clear connection pools after a transaction.

    Explanation: runMigration is used to update the actual database schema so that it matches the entities as defined in Haskell. It does not delete all tables, create in-memory-only relationships, or clear connection pools. The mechanism ensures database and code are synchronized, which is vital for data integrity.

  5. PersistField Typeclass Role

    Why is it necessary to implement the PersistField typeclass for certain custom Haskell types in Persistent?

    1. To allow direct use of raw SQL strings in all functions.
    2. To automatically generate random data for testing.
    3. To enable conversion between Haskell types and database values.
    4. To improve the runtime speed of all queries.

    Explanation: Persistent needs to know how to convert custom Haskell types to and from database values, and this is done by implementing the PersistField typeclass. This does not affect query speed, generate test data, or impact use of raw SQL. The other options either misstate the purpose or relate to unrelated features.

  6. Esqueleto Query Result Typing

    What is the result type when you execute an Esqueleto query using the select function?

    1. A list of values matching the selected fields, often in tuples.
    2. A string format of the raw SQL query.
    3. A map of field names to values for all entities.
    4. A single value wrapped in Maybe.

    Explanation: Esqueleto’s select returns a list where each element matches the query's result fields, frequently as tuples if multiple entities or fields are selected. It does not produce maps, single Maybe values, or raw SQL strings. Only a list of the selected values represents the actual output.

  7. Adding Constraints in Persistent

    How would you specify a uniqueness constraint for an email field in a Persistent entity definition?

    1. By adding a unique=True field annotation.
    2. By writing enforceUnique email under the entity.
    3. By calling enforceConstraint in the migration file.
    4. By adding UniqueEmail email in the quasi-quoted block.

    Explanation: In Persistent, you add a line like UniqueEmail email in the entity's quasi-quoted block to create a uniqueness constraint on the email field. There is no unique=True annotation, and functions like enforceUnique or enforceConstraint are not part of Persistent's schema definition process. The accepted method is the Unique line.

  8. Persistent and Automatic Key Generation

    A new database row is inserted via Persistent without specifying a primary key. How does Persistent handle the key in this case?

    1. Persistent uses a random Haskell Int value as the key.
    2. Persistent asks the user for a key at runtime.
    3. Persistent throws an error and halts the insertion.
    4. Persistent relies on the database to automatically generate the primary key.

    Explanation: For auto-incrementing keys, Persistent lets the database generate the primary key upon insertion. It does not throw an error, use a random Int, or prompt the user for input. Automatic key handling is managed according to the database schema settings.

  9. Filtering Queries in Esqueleto

    Which construct is used in Esqueleto to add a WHERE clause that filters users by age greater than 18?

    1. You set the filterField attribute before select.
    2. You write the whereClause in a query string.
    3. You add filter_ to the query directly.
    4. You apply the where_ function with a suitable expression.

    Explanation: Esqueleto uses the where_ function to express the WHERE part of a query with the desired condition, such as checking if age is greater than 18. The functions filter_ and filterField do not exist, and writing whereClause as a string is not how Esqueleto's EDSL operates.

  10. Persistent Selectors and Pattern Matching

    When retrieving an Entity value from a Persistent query, how do you access the entity’s field values in Haskell?

    1. By using index numbers as if it were a tuple.
    2. By calling getField function on the Entity.
    3. By referencing the database column name in a string.
    4. By pattern matching to get the record and then using field accessors.

    Explanation: You pattern match on the Entity to extract the underlying record, then use standard Haskell field accessor functions. There is no getField function or tuple-based indexing, and referencing database column names as strings is not a Haskell pattern. Pattern matching provides type-safe access.