2D Convex hull exercise
Terminology

In your hull function, you use the name origin to indicate the chosen point with the minimum y coordinate.  That's confusing, as the term usually means the point (0, 0) in the Cartesian plane.  For the rest of this review, I'm going to refer to the chosen point as the pivot instead.

Wrong result

The test result that you quoted in your own question shows that the algorithm is incorrect!



Furthermore, when I ran your code with the suggested test case, I got a totally different result than what you claimed, which makes it hard for me to explain exactly what went wrong.  (The code produces results in clockwise order, contrary to your claimed answer which lists them in counterclockwise order.  I seriously doubt that you posted the question correctly.)  I can, however, point to four issues:


(drop 1 list) was responsible for removing (-4, 1) from consideration.
A correct traversal involves backtracking whenever a right turn occurs.  Therefore, you must filter the candidate points while you traverse the list.  You cannot simply traverse the list and keep just the vertices where a left turn occurred, since a left turn that occurs at an interior point doesn't mean that it is a vertex of the convex hull.
Reporting points that lie collinearly on the convex hull (but not at a vertex) is a difficult task with this algorithm, since it is hard to determine in advance the order in which to consider such points such that they will be guaranteed to be part of a left-turn-only tour.
Degenerate cases are poorly handled.  hull of a single point lists that point twice.  hull of a triangle can also list the pivot twice.


Implementation

Stuff like this is hard to follow:


angle :: Point -&gt; Point -&gt; Point -&gt; Float
angle a b c = - (bax * bcx + bay * bcy) /
              (sqrt ((bax * bax + bay * bay) * (bcx * bcx + bcy * bcy)))
    -- more efficient than acos for the algorithm and is the same for sorting
    where
      bax = x a - x b
      bay = y a - y b -- coordinates of b-&gt;a
      bcx = x c - x b
      bcy = y c - y b -- coordinates of b-&gt;c



A remedy is to use a vector package to allow you to think at a more abstract level.

angle a b c :: Vector2 -&gt; Vector2 -&gt; Vector2 -&gt; Double
angle a b c = (ab `vdot` bc) / ((vmag ab) * (vmag bc))
  where
    ab = b - a
    bc = c - b


In the implementation below, I've rewritten the function further, restricting it to only compare a single vector against \$\mathbf{\hat{x}}\$, and renaming it to cosAngleX to be more honest.

You don't really need to define the argmin function.  argmin y pts could just be written as minimumBy (comparing y) pts, using support from Data.List.

Suggested solution

import Data.List (minimumBy, sortBy)
import Data.Ord (comparing)

-- https://hackage.haskell.org/package/AC-Vector
import Data.Vector.Class (vdot, vmag)
import Data.Vector.V2 (Vector2(..), v2x, v2y)
import Data.Vector.V3 (Vector3(..), v3z, vcross)

data Direction = TurnLeft
               | TurnRight
               | StraightLine
                 deriving (Show, Eq)

-- Direction of the turn when travelling from a to b to c
turn :: Vector2 -&gt; Vector2 -&gt; Vector2 -&gt; Direction
turn a b c
  | z &gt; 0     = TurnLeft
  | z &lt; 0     = TurnRight
  | otherwise = StraightLine
  where
    ba = a - b
    bc = c - b
    promote23 (Vector2 x y) = Vector3 x y 0
    z = v3z ((promote23 bc) `vcross` (promote23 ba))

-- Cosine of the angle that a vector forms with the X axis.
-- We can sort by this instead of sorting by the angle as long as all vectors
-- have a nonnegative y coordinate (which is the case, since we picked a point
-- with the minimum y coordinate as the pivot).
-- For (Vector2 0 0), the result is Infinity
cosAngleX :: Vector2 -&gt; Double
cosAngleX v
  | norm == 0  = 1/0
  | otherwise  = (v `vdot` xHat) / norm
  where
    xHat = Vector2 1 0
    norm = vmag v

giftWrap :: [Vector2] -&gt; [Vector2]
giftWrap vs = result
  where
    -- Decides whether to keep or reject vertex b.
    -- Vertex b is provisionally kept if a left turn occurs there; the result
    -- is finalized only when a closed left-turn-only tour is completed.
    giftWrap' (a:b:c:vs)
      | vs == [] &amp;&amp; dir == TurnLeft  = (True, [b, c])
      | vs == []                     = (False, [c])
      | dir == TurnLeft &amp;&amp; final     = (True, (b:gw))
      | dir == TurnLeft              = giftWrap' (a:gw)
      | otherwise                    = (False, (a : c : vs))
      where
        dir = turn a b c
        (final, gw) = giftWrap' (b:c:vs)
    (_, result) = giftWrap' (vs ++ [head vs])

-- Chooses the point with the smallest y coordinate (breaking ties by smallest
-- x coordinate), then presents the points by the angle between the x axis,
-- the pivot, and each point.  The pivot will be the last element of the
-- resulting list.
chain :: [Vector2] -&gt; [Vector2]
chain pts = sortBy (comparing byAngleX) pts
  where
    comparing2 primaryKey secondaryKey a b
      | firstCmp == EQ  = comparing secondaryKey a b
      | otherwise       = firstCmp
      where firstCmp = comparing primaryKey a b
    pivot      = minimumBy (comparing2 v2y v2x) pts
    byAngleX v = -cosAngleX (v - pivot)

-- Convex hull in some counter-clockwise order
hull :: [Vector2] -&gt; [Vector2]
hull (a:[])    = [a]
hull (a:b:[])  = [a, b]
hull vs        = giftWrap $ chain vs


The test case:

*Main&gt; hull [(Vector2 (-3)   1),  (Vector2 (-4) 1),   (Vector2 (-1)  4),
             (Vector2   0    0),  (Vector2   2  2),   (Vector2 (-1)  3),
             (Vector2 (-1)   2),  (Vector2   1  0),   (Vector2   3 (-1)),
             (Vector2 (-1) (-1))]
[Vector2 {v2x = 3.0, v2y = -1.0}, Vector2 {v2x = 2.0, v2y = 2.0},
 Vector2 {v2x = -1.0, v2y = 4.0}, Vector2 {v2x = -4.0, v2y = 1.0},
 Vector2 {v2x = -1.0, v2y = -1.0}]