text: Title: accept dotted path in grid arguments
  Body: grid allows users to use the same source and execute it with many parameters. this is typically used to train ML models with a grid of parameters:
  
  ```yaml
  tasks:
    - source: random-forest.py
      # name is required when using grid
      name: random-forest-
      product: random-forest.html
      grid:
          n_estimators: [5, 10, 20]
          criterion: [gini, entropy]
  ```
  
  however, creating large arrays of tasks is inconvenient since they have to list all values. we should allow them define a function that generates them:
  
  ```yaml
  tasks:
    - source: random-forest.py
      # name is required when using grid
      name: random-forest-
      product: random-forest.html
      grid:
          n_estimators: generate.estimators
          criterion: [gini, entropy]
  ```
  
  ```python
  # generators.py
  
  def estimators():
      return list(range(1, 100)) # this returns 100 values!
  ```
  
  we should also allow passing arguments to the dotted path:
  
  
  ```yaml
  tasks:
    - source: random-forest.py
      # name is required when using grid
      name: random-forest-
      product: random-forest.html
      grid:
          n_estimators:
            dotted_path: generate.estimators
            min: 1
            max: 100
            step: 2
          criterion: [gini, entropy]
  ```
  
  and support this as well when grid is a list:
  
  ```yaml
  tasks:
    - source: random-forest.py
      # name is required when using grid
      name: random-forest-
      product: random-forest.html
      grid:
          - n_estimators: generate.estimators
            criterion: [gini, entropy]
  ```
  
  
  ```python
  # generators.py
  
  def estimators(min, max, step):
      return list(range(min, max, step))
  ```
  