Documenting a function that is created inside a type


#1

Dear all,

Any idea on how I can make the documentation for this function called utility accesible to the user once an instance of this type has been created?

immutable Fundamentals
	# Calibrated
	α::Float64				# Utility cost of working
	β::Float64       		# Discount factor
	γ_bar::Float64 			# Average search cost
	ϵ_γ::Float64			# Standard deviation search cost
	ρ_z::Float64			# Persistance productivity process
	σ_ϵ::Float64			# Standard deviation productivity process
	σ_q::Float64			# Standard deviation match quality process
	λ_e::Float64			# Probability of finding another job for employed agents
	λ_u::Float64			# Probability of finding a job for unemployed agents
	λ_n::Float64			# Probability of finding a jon for OLF agents
	σ::Float64				# Probability of losing a job for employed agents
	# Assigned
	μ::Float64				# Average duration
	b_0::Float64			# Default replacement ratio
	b_bar::Float64			# Benefits cap
	θ::Float64				# Capital share of output in the aggregate production function
	δ::Float64				# Capital depreciation
	τ::Float64				# Proportional tax on labor income
	# Numerical
	gp_a::Int64 			# Grid points for assets
	gp_z::Int64				# Grid points for match productivity process
	gp_q::Int64				# Grid points for match quality process
	gp_γ::Int64				# Grid points for match grid points for search effort
	min_a::Float64 			# Minimum level of assets
	max_a::Float64			# Maximum level of assets
	cover_z::Int64			# Number of standard deviations to each side the productivity process
	cover_q::Int64			# Number of standard deviations to each side the match quality process
	# Grids
	a_values::Vector{Float64} 	# Values for assets
	z_values::Vector{Float64} 	# Values producitivy process
	q_values::Vector{Float64} 	# Values match quality process
	γ_values::Vector{Float64}	# Values search cost process
	Iᴮ_values::Vector{Bool}		# Values UI status
	# Tranistion matrices
	z_z′::Array{Float64,2}		# Productivity process
	q_q′::Vector{Float64} 		# Match quality
	γ_γ′::Vector{Float64} 		# Search costs
	Iᴮ_Iᴮ′::Array{Float64,2}	# UI
	# Utility
	utility::Function  			# Utility function

	function Fundamentals(;
		α::Float64 = 0.485,
		β::Float64 = 0.99465,
		γ_bar::Float64 = 0.042,
		ϵ_γ::Float64 = 0.030,
		ρ_z::Float64 = 0.996,
		σ_ϵ::Float64 = 0.096,
		σ_q::Float64 = 0.034,
		λ_e::Float64 = 0.121,
		λ_u::Float64 = 0.278,
		λ_n::Float64 = 0.182,
		σ::Float64 = 0.0178,
		μ::Float64 = 1.0/6.0,
		b_0::Float64 = 0.23,
		b_bar::Float64 = 0.465,
		θ::Float64 = 0.3,
		δ::Float64 = 0.0067,
		τ::Float64 = 0.3,
		gp_a::Int64 = 48,
		gp_q::Int64 = 7,
		gp_z::Int64 = 20,
		gp_γ::Int64 = 3,
		min_a::Float64 = 0.0,
		max_a::Float64 = 1440.0,
		cover_z::Int64 = 2,
		cover_q::Int64 = 2
			)
		# Grid for assets
		a_values = loggrid(min_a, max_a, gp_a)

		# Productivity process
		z_process = tauchen(gp_z, ρ_z, σ_ϵ, 0.0, cover_z)
	 	z_values = exp.(z_process.state_values)   	# Unpack vector values associated with states
		z_z′ = z_process.p                        	# Unpack transition matrix

		# Match quality process
		q_process = tauchen(gp_q, 0.0, σ_q, 0.0, cover_q)
		q_values = exp.(q_process.state_values)   	# Unpack vector values associated with states
		q_q′ = q_process.p[1,:]                   	# Unpack probability distribution (ρ = 0.0)

		# Search cost process
		γ_values = [γ_bar - ϵ_γ, γ_bar, γ_bar + ϵ_γ]          # Vector of search costs
		γ_γ′ = fill(1.0/length(γ_values), length(γ_values))   # Unifrom probability distribution

		# UI process
		Iᴮ_values = [true, false]                 # Vector of UI indicator values
		Iᴮ_Iᴮ′ = [μ 1.0-μ; 0.0 1.0]               # Tranistion matrix UI eligibility

		# Utility function
		"""
		    utility(consumption[, works, searches, ind_γ])

		Compute instantaneous utility as in Krusell et al.

		# Arguments
		- `consumption::Float64` : amount of consumption
		- `works::Bool` : whether the agent works
		- `searches:: Bool` : whether the agent seraches
		- `ind_γ::Int64` : index of the serach shock
		"""
		utility = function(
		            consumption::Float64,
		            works::Bool = false,
		            searches::Bool = false,
		            ind_γ::Int64 = 0
		            )
		    if searches
		        if ind_γ > 0
		            aux_search = γ_values[ind_γ]*searches
		        else
		            throw(ArgumentError("When searches is true,
		                                realisation_γ needs to be a valid index for γ"))
		        end
		        if works
		            throw(ArgumentError("Agent cannot work and search simultaneously"))
		        end
		    else
		        aux_search = 0.
		    end

		    if consumption <= 0.
		        return -1e10
		    else
		        return log(consumption) - (α*works) - aux_search
		    end
		end

		new(α, β, γ_bar, ϵ_γ, ρ_z, σ_ϵ, σ_q, λ_e, λ_u, λ_n, σ,
			μ, b_0, b_bar, θ, δ, τ,
			gp_a, gp_z, gp_q, gp_γ, min_a, max_a, cover_z, cover_q,
			a_values, z_values, q_values, γ_values, Iᴮ_values,
			z_z′, q_q′, γ_γ′, Iᴮ_Iᴮ′,
			utility)
	end
end

#2

A minimal example would be kinder on the people potentially helping you!

You can attach docstrings to fields, maybe that helps? Alternatively, if you define the function in the global scope and add the docstring to that?

Also, from your non-minimal example, it looks like you could use https://github.com/mauro3/Parameters.jl.


#4

Thanks, Mauro.

I want to use as little globals as possible because I need this code to be as fast as possible.

Here is a simplified example:

immutable Fundamentals
	σ::Float64
	utility::Function

	function Fundamentals(σ::Float64)
		"""
		I want to be able to describe the function created here
		"""
		utlity = function(consumption::Float64)
					if σ == 1
						return log(consumption)
					else
						return consumption^(1.-sigma)
					end
		new(σ, utility)
	end
end

#5

Having a utility::Function field will make it very slow since that is a non concrete type.

Either have

struct Fundamentals{F}
    σ::Float64
    utility::F  
end

or make a somthing like this (which I would say is much better)

utility(f::Fundamentals) = f.σ == 1 ? log(consumption) : consumption^(1 - f.σ)

#6

Thank you, Kristoffer. This was just a simplified example. My original post describes the function I need to deal with. I’m not sure I can apply your solution to that.


#7

Sure, something like

function utility(
        f::Fundamentals,
        consumption::Float64,
        works::Bool = false,
        searches::Bool = false,
        ind_γ::Int64 = 0
        )
    if searches
        if ind_γ > 0
            aux_search = f.γ_values[ind_γ] * searches
        else
            throw(ArgumentError("When searches is true,
                                realisation_γ needs to be a valid index for γ"))
        end
        if works
            throw(ArgumentError("Agent cannot work and search simultaneously"))
        end
    else
        aux_search = 0.
    end

    if consumption <= 0.
        return -1e10
    else
        return log(consumption) - (f.α*works) - aux_search
    end
end

#8

I see. From your reply, I understand that it’s better (in terms of performance) to pass the entire structure of Fundamentals to a function rather than having a function as an “output” of a structure. This is true even if the functions uses very “few” of the elements contained in the Fundamentals?

Thanks!


#9

If you are worried about performance, you will need to do benchmarking and profiling. That way you will also acquire a feel for the performance of Julia. Checkout BenchmarkTools.jl and the profiling section in the docs.