code: def concatenate(nds, axis=0):
      """Join a sequence of arrays along an existing axis.
  
      Examples
      --------
  
      >>> x = hl.nd.array([[1., 2.], [3., 4.]])
      >>> y = hl.nd.array([[5.], [6.]])
      >>> hl.eval(hl.nd.concatenate([x, y], axis=1))
      array([[1., 2., 5.],
             [3., 4., 6.]])
      >>> x = hl.nd.array([1., 2.])
      >>> y = hl.nd.array([3., 4.])
      >>> hl.eval(hl.nd.concatenate((x, y), axis=0))
      array([1., 2., 3., 4.])
  
      Parameters
      ----------
      nds : a sequence of :class:`.NDArrayNumericExpression`
          The arrays must have the same shape, except in the dimension corresponding to axis (the first, by default).
          Note: unlike Numpy, the numerical element type of each array_like must match.
      axis : int, optional
          The axis along which the arrays will be joined. Default is 0.
          Note: unlike Numpy, if provided, axis cannot be None.
  
      Returns
      -------
      :class:`.NDArrayExpression`
          The concatenated array
      """
      head_nd = nds[0]
  
      if isinstance(nds, list):
          indices, aggregations = unify_all(*nds)
          typs = {x.dtype for x in nds}
  
          if len(typs) != 1:
              element_types = {t.element_type for t in typs}
              if len(element_types) != 1:
                  argument_element_types_str = ", ".join(
                      str(nd.dtype.element_type) for nd in nds
                  )
                  raise ValueError(
                      f"hl.nd.concatenate: ndarrays must have same element types, found these element types: ({argument_element_types_str})"
                  )
  
              ndims = {t.ndim for t in typs}
              assert len(ndims) != 1
              ndims_str = ", ".join(str(nd.dtype.ndim) for nd in nds)
              raise ValueError(
                  f"hl.nd.concatenate: ndarrays must have same number of dimensions, found: {ndims_str}."
              )
      else:
          indices = nds._indices
          aggregations = nds._aggregations
  
      makearr = aarray(nds)
      concat_ir = NDArrayConcat(makearr._ir, axis)
  
      return construct_expr(
          concat_ir,
          tndarray(head_nd._type.element_type, head_nd.ndim),
          indices,
          aggregations,
      )
  
imports: from functools import reduce
  import hail
  from hail.expr.functions import _ndarray
  from hail.expr.functions import array
  from hail.expr.types import HailType, tfloat64, tfloat32, ttuple, tndarray
  from hail.typecheck import typecheck, nullable, oneof, tupleof, sequenceof
  from hail.expr.expressions import expr_int32, expr_int64, expr_tuple, expr_any, expr_array, expr_ndarray, expr_numeric, Int64Expression, cast_expr, construct_expr, expr_bool, unify_all
  from hail.expr.expressions.typed_expressions import NDArrayNumericExpression
  from hail.ir import NDArrayQR, NDArrayInv, NDArrayConcat, NDArraySVD, NDArrayEigh, Apply
label_desc: Use image node_id 15 for calling a global function with example usage: concatenate(nds, axis) and returns: construct_expr